Sunday, 11 October 2026

Start up nginx Docker container and edit homepage

This article will be a quick tip how to get a web server up and running on your system, in this case using Docker image nginx to spin up a container. First off, install Docker. To install docker:
  • The Docker website contains the container software you need to quickly spin up nginx. You need an account there, you can use for example a Google account to log in and create the account
  • You probably want to use for just experimentation the Personal plan, which is free. Go to https://docs.docker.com/get-started/get-docker/
  • Install Docker for Desktop for your OS - Linux, Windows or Mac
To get nginx up and running, do the following commands from the command line, after Docker is installed and Docker for Desktop has been started. This will make sure the Docker background system processes are running (Docker deaemon).

docker command (CLI)
        │
        ▼
Docker daemon (dockerd)
        │
        ├─ Creates containers
        ├─ Pulls images
        ├─ Creates networks
        ├─ Manages volumes
        └─ Starts/stops containers
        
 

Links to Nginx and Docker hub Nginx image



https://nginx.org

The docker hub image for nginx is here, it has been downloaded a billion times plus.

https://hub.docker.com/_/nginx

Getting nginx running on your system

First off we start the nginx image (it will be pulled the first time and downloaded, then cached later on). It is run as a daemon background service with -d. nginx will run at port 8080 to avoid conflicting with other services running at port 80 such as IIS web server. The nginx project's homepage is here:


docker run -d --name nginx_test -p 8080:80 nginx

Here, we name the container as nginx_test The system prompts back the container id We can also see the containers running image with name nginx like this :

docker ps --filter ancestor=nginx


CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                     NAMES
a2cc9ed9b212   nginx     "/docker-entrypoint.…"   12 minutes ago   Up 12 minutes   0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   nginx_test
ea3bf3541852   nginx     "/docker-entrypoint.…"   12 hours ago     Up 12 hours     80/tcp                                    container-b
505eec83cca5   nginx     "/docker-entrypoint.…"   12 hours ago     Up 12 hours     80/tcp   

In this case, I tested some nginx containers some hours ago and they are still running. We can stop containers by running docker stop <container-id> Our nginx_test container is now running, we can enter the container next.

docker exec -it nginx_test bash

The Linux OS that is running inside the nginx container has no editor installed, not even vim og vi (it is optimalized to use minimal disk space so it got the bare minimum to run the web server nginx itself). Lets install nano to edit files inside the container!

apt-get update
apt-get install nano

The homepage of nginx is installed in /usr/share/nginx/html Edit the index.html file by running nano . nano index.html

cd /usr/share/nginx/html
nano index.html

You can save the index.html file after editing in nano by entering Ctrl+O and write to the file.




Additional notes

This is just for testing out nginx. To stop all containers on your system for example, you can either exit out of Docker for desktop or run this command (Powershell, other OS-es got similar command):


docker stop $(docker ps -q)


This will output the container ids of what was stopped. Afterwards, you can check that no containers are running using this:


docker ps


You should see an empty list of containers, i.e. no containers running on your system.
 ~#@❯ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
Just to note, if you are to run nginx on a server you most definately want to enable a drive for your container, for durability. But for now, this article was just a short article how to get a web server such as nginx up and running on your system and play around with Docker. Windows Users - Please note Docker takes much resources, so to stop Docker in Windows and if you run the default setup of using WSL2 containers, this command should stop Docker. You can start up Docker again by just running Docker Desktop again.

wsl --shutdown
taskkill /F /IM "Docker Desktop.exe"

Tuesday, 6 October 2026

Fire and forget - Try catch and the importance of awaiting async methods

Async void methods are sometimes used in fire and forget scenarios and in event handlers in application code that uses frameworks such as Windows Forms or WPF. Consider the following C# code :


async Task Main()
{
	try
	{
		Operations.TidyCache("");		
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}	                                                                                                                                                                                                                                                                                                                                                                                                                         
}

public static class Operations {
	
	
	public static async void TidyCache(string key){
		
		ArgumentException.ThrowIfNullOrEmpty(key);
		await Task.Run(async() =>
		{
			await System.Threading.Tasks.Task.Delay(500);
			Console.WriteLine("Finished running async task : waited half a second above");
		});
		
		
	}
}


Output from code above :
Completed Successfully ArgumentException (...) The value cannot be an empty string. (Parameter 'key')


The problem with the code shown above is that although we do an initial error of passing in an empty string as the parameter key to method TidyCache, our try-catch will not be actually catched. The reason for this is that our async method is called and entered and the ArgumentException is thrown inside the async state machin and therefore thrown outside the try/catch. The end result is that since we do not await the method either, the caller will not get the chance to catch the exception and the exception is thrown directly on the thread pool and can also in some applications lead to application crash. We can move the try catch inside the TidyCache method and let the async method return Task instead of void. The calling code uses a discard assignment.

First solution



async Task Main()
{
	try
	{
		_ =Operations.TidyCache("");
	
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}
}

public static class Operations
{

	public static async Task TidyCache(string key)
	{
		try
		{
			ArgumentException.ThrowIfNullOrEmpty(key);
			await Task.Delay(500);
			Console.WriteLine("Finished running async task");
		}
		catch (Exception ex)
		{
			Console.Error.WriteLine($"TidyCache failed: {ex.Message}");
		}
	}

}


Output from the code above :
TidyCache failed: The value cannot be an empty string. (Parameter 'key') Completed Successfully

Second solution

The first solution is the correct pattern for fire and forget pattern. The second solution shown below is the right way to handle exception for async methods by allowing the async task to flow its exception up to the caller and actually await the async method.


async Task Main()
{
	try
	{
		await Operations.TidyCache("");
		Console.WriteLine("Completed Successfully");
	}
	catch (Exception ex)
	{
		Console.Error.WriteLine($"Critical error: {ex.Message}"); //this try-catch will not be entered due to lack of awaiting Operations.TidyCache
	}	                                                                                                                                                                                                                                                                                                                                                                                                                                                   
}

public static class Operations {	
	
	public static async Task TidyCache(string key){
		
		ArgumentException.ThrowIfNullOrEmpty(key);
		await Task.Run(async() =>
		{
			await System.Threading.Tasks.Task.Delay(500);
			Console.WriteLine("Finished running async task : waited half a second above");
		});			
	}
	
}


Output from code above :
Critical error: The value cannot be an empty string. (Parameter 'key')


The second solution shows the behavior and execution flow most programmers will expect from the code. The exception inside the async method that is thrown is catched in the calling code. Finally some (block) quotes about async void methods:

“async void is where exceptions go to die.”

“async void is the ghost of a Task — it haunts your runtime.”

“If you think you need async void, you probably need therapy.”

“async void is like fire-and-forget — except you forget the fire.”

“Use async void when you want exceptions to bypass your try/catch like a VIP.”

Wednesday, 16 September 2026

Creating 2d scatter plots with Rust and Plotly

This article shows how 2d scatter graphs can be created using Rust and Plotly crate. The following screenshot shows generated 2d scatter graph. Plotly is very powerful graph library with a lot of graphs supporting 2d and 3d data visualization. We can use Plotly to generate the plots / graphs / diagrams into rasterized pictures, default format is PNG format. In the demo, the plot is displayed using a html file and the Plotly Javascript - Js - library. The html file shows controls for exporting the Plotly plot / graph / diagram to a picture. Default format is again PNG in image export. You can use Plotly and Rust to generate pictures or HTML in the background and display them in a web browser or any other client supporting graphical display of plots or pictures.
Plotly is a data visualization library that enables users to create interactive, publication ready charts and dashboards in Python, R and JavaScript. It is widely used for exploratory data analysis, business reporting and web‑based visualisations.
The Plotly website is available at the following url:

https://plotly.com/python/



Github repo with source code for the demo

My Github repo with the demo source code in this article is available here:
https://github.com/toreaurstadboss/RustWithPlotly2dDemo

Screenshot of the demo
Shown below is a screenshot running the demo. A scatter graph with 3 data series are shown. A color scale with palette Viridis is shown. The color scale also controls the color of the points depending on the point's y value.



Setting up the Plotly crate

Cargo add Plotly adds the Plotly crate. Your Rust application can then star using Plotly. Please note that Plotly crate can let you output pictures, which then can be shown in a console application or a web site for example.

Rust manifest file

The Rust manifest file - that is Cargo.toml file - looks like this :
Cargo.toml

[package]
name = "plotly_plotdemo2d"
version = "0.1.0"
edition = "2026"

[dependencies]
plotly = "0.14.1"
rand = "0.10.2"





Helper methods for creating the 2d scatter plot

The following helper methods sets up shared look of axis and series (called 'traces' in Plotly) font sizes and injects HTML script for a shuffle button to regenerate 2d scatter plots with 3 series with randomized y-values. Also, setting up page background style and overall container layout in the HTML is also shown below.
main.rs


fn make_scatter_series(series_name: &str) -> Box<Scatter<i32, i32>> {

    let xpoints: Vec<i32> = (1..=10).collect();
    let ypoints: Vec<i32> = (1..=10)
        .map(|_| rand::random_range(-80..=80))
        .collect();

    let serieslabels: Vec<String> = xpoints
        .iter()
        .zip(&ypoints)
        .map(|(x, y)| format!("({}, {})", x, y))
        .collect();
    let seriesmarker = series_marker(series_name == "Series 1", &ypoints);

     let seriestrace = Scatter::new(xpoints, ypoints)
        .mode(Mode::LinesMarkersText)
        .name(series_name)
        .text_array(serieslabels)
        .text_position(Position::TopCenter)
        .text_font(trace_font())
        .hover_template("(%{x}, %{y})<extra></extra>")
        .marker(seriesmarker);

     seriestrace
}

/* Helper methods for drawing the 2d scatter plot */

fn axis_font() -> Font {
    Font::new()
        .family("Aptos, Segoe UI, Arial, sans-serif")
        .size(16)
        .color("#F8FAFC")
}

fn trace_font() -> Font {
    Font::new()
        .family("Aptos, Segoe UI, Arial, sans-serif")
        .size(11)
        .color("#E2E8F0")
}

fn series_marker(show_scale: bool, ypoints: &[i32]) -> Marker {
    let marker = Marker::new()
        .size(10)
        .color_array(ypoints.to_vec())
        .color_scale(ColorScale::Palette(ColorScalePalette::Viridis))
        .show_scale(show_scale);

    if show_scale {
        marker.color_bar(ColorBar::new().title("Intensity (Y value)"))
    } else {
        marker
    }
}

fn apply_page_background(output_file: &PathBuf) {
    let html = fs::read_to_string(output_file).expect("failed to read generated Plotly HTML");
    let styled_html = html
        .replacen("height:100%; width:100%;", "height:100%; width:50%; margin:0 auto;", 1)
        .replacen(
        "<body>",
        &format!(r#"<body style="{}">"#, PAGE_BACKGROUND_STYLE),
        1,
    );

    fs::write(output_file, styled_html).expect("failed to rewrite Plotly HTML with page styling");
}

fn inject_shuffle_button_and_script(output_file: &PathBuf) {
    let cargo_root = env!("CARGO_MANIFEST_DIR");
    let shuffle_block = std::fs::read_to_string(Path::new(cargo_root).join("assets/shuffle_2dscatter_plot.html")).expect("failed to read shuffle block HTML");

    let html = fs::read_to_string(output_file).expect("failed to read generated Plotly HTML");
    let updated_html = html.replacen("</body>", &format!("{}\n</body>", shuffle_block), 1);

    fs::write(output_file, updated_html).expect("failed to inject shuffle button and script");
}




Main program

The plot is written to html using the plotly::Plot method write_html, this writes the plot into a html file with script references to the Plotly Js library and SVG export Js lib. The code checks if target OS is windows and if so, spawns a new process using std::Command::new("cmd") with arguments pased in to start the file. This will show a dialog box of which browser to open the file and if that is already configured, opens the generated html file into the configured web browser to handle html files in the OS.
main.rs



use plotly::{
    common::{Anchor, ColorBar, ColorScale, ColorScalePalette, Font, Marker, Mode, Orientation, Position, Title},
    layout::{Axis, Legend},
    Layout, Plot, Scatter,
};
use std::{fs, path::PathBuf, path::Path, process::Command};

const DTICK_SIZE: f64 = 10.0;
const DTICK_SIZE_X_AXIS: f64 = 1.0;
const OUTPUT_FILE_NAME: &str = "2dscatter.html";
const PAGE_BACKGROUND_STYLE: &str =
    r#"margin:0; min-height:100vh; background: linear-gradient(135deg, #081120 0%, #132238 45%, #050816 100%); font-family: 'Aptos', 'Segoe UI', Arial, sans-serif;"#;


fn main() {
    println!("Generating a 2d scatter plot using Plotly and Rust - Demo");

    let grid_color: &str = "rgba(148, 163, 184, 0.35)";

    let mut plot = Plot::new();

    for i in 1..=3 {
        let series_trace = make_scatter_series(&format!("Series {}", i));
        plot.add_trace(series_trace);
    }

    plot.set_layout(
        Layout::new()
            .title(
                Title::with_text("2D scatter plot with Plotly and Rust").font(
                    Font::new()
                        .family("Aptos, Segoe UI, Arial, sans-serif")
                        .size(24)
                        .color("#F8FAFC")
            ))
            .font(
                Font::new()
                    .family("Aptos, Segoe UI, Arial, sans-serif")
                    .size(14)
                    .color("#E2E8F0"))
            .legend(
                Legend::new()
                    .orientation(Orientation::Horizontal)
                    .x(0.0)
                    .x_anchor(Anchor::Left)
                    .y(-0.22)
                    .y_anchor(Anchor::Top),
            )
            .x_axis(
                Axis::new()
                    .title(Title::with_text("X Axis").font(axis_font()))
                    .dtick(DTICK_SIZE_X_AXIS)
                    .show_grid(true)
                    .grid_color(grid_color))
            .y_axis(
                Axis::new()
                    .title(Title::with_text("Y Axis").font(axis_font()))
                    .dtick(DTICK_SIZE)
                    .show_grid(true)
                    .grid_color(grid_color))
            .paper_background_color("rgba(0, 0, 0, 0)")
            .plot_background_color("rgba(15, 23, 42, 0.82)"));

    let output_file = PathBuf::from(OUTPUT_FILE_NAME);
    plot.write_html(&output_file);
    apply_page_background(&output_file);
    inject_shuffle_button_and_script(&output_file);

    #[cfg(target_os = "windows")]
    {
        println!("Opening 2d scatter plot..");
        let _ = Command::new("cmd")
            .args(["/C", "start", "", output_file.to_str().unwrap()])
            .spawn();
    }

}




Shuffle button html static file

To provide generating sample data, a static html file containing a little bit Javascript is used to provide some dynamic reload capability. Randomized data is generated. We use Javascript and not Rust to regenerate the series for the Plotly plot showing the 3 data series in the scatter x,y plot.
Shuffle_2d_Scatter_plot.html


<button
  id="shuffle-button"
  style="
    position: fixed;
    top: 16px;
    left: 16px;
    z-index: 1000;
    padding: 10px 14px;
    border: 0;
    border-radius: 10px;
    background: #38bdf8;
    color: #081120;
    font:
      600 14px "Aptos",
      "Segoe UI",
      Arial,
      sans-serif;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.35);
  "
>
  Shuffle series
</button>
<script>
  const graphDiv = document.getElementById("plotly-html-element");

  function buildSeries(seriesName, showScale) {
    const xValues = Array.from({ length: 10 }, (_, index) => index + 1);
    const yValues = Array.from(
      { length: 10 },
      () => Math.floor(Math.random() * 161) - 80,
    );

    return {
      x: xValues,
      y: yValues,
      mode: "lines+markers+text",
      name: seriesName,
      text: xValues.map((xValue, index) => `(${xValue}, ${yValues[index]})`),
      textposition: "top center", 
      hovertemplate: "(%{x}, %{y})<extra></extra>",
      textfont: {
        family: "Aptos, Segoe UI, Arial, sans-serif",
        size: 11,
        color: "#E2E8F0",
      },
      marker: {
        size: 10,
        color: yValues,
        colorscale: "Viridis",
        showscale: showScale,
        colorbar : {
            title: {
                text: "Intensity (Y-value)",
            }
        },
      },
    };
  }

  function shuffleSeries() {
    Plotly.react(
      graphDiv,
      [
        buildSeries("Series 1", true),
        buildSeries("Series 2", false),
        buildSeries("Series 3", false),
      ],
      graphDiv.layout,
    );
  }

  document
    .getElementById("shuffle-button")
    .addEventListener("click", shuffleSeries); 
</script>



StackBlitz demo running the Rust+Plotly demo

A StackBlitz demo is available online here that shows how we can build the Rust code into Wasm Web Assembly and use Vite to display the demo.

https://stackblitz.com/~/github.com/toreaurstadboss/rust-wasm-plotly?file=rust/src/scatter_demo.rs

The Github repo for the StackBlitz based demo is here:

https://github.com/toreaurstadboss/rust-wasm-plotly

Screenshot of StackBlitz running demo :