When deploying a .net application to a docker container you would commonly use a Dockerfile, build the Docker image then push the Docker image to a registry, pull the whole image from the registry again to the server and run the image.
There are many benefits in doing it that way, especially in larger production environments. But when you’re just testing, deploying to small production or staging environments or just want to run something on a Raspberry PI, there are also drawbacks that can make deployment more time-consuming and complicated than required. These include:
- Having to build the whole image, not just your binaries.
- Having to write and manage a Dockerfile.
- Having Docker installed on the build machine (not every hardware supports or allows virtualization) or setting up a dedicated build pipeline.
- Having to pull the base image to the build machine, uploading and then downloading the whole Docker image.
- Having to set up a registry where you can upload your image.
- Having to rebuild your Docker image when you want to apply an update of the base image (e.g. to close a security vulnerability).
- You might not be able to use every image depending on the registry you want to use.
- Depending on your environment you might run into limitations. You might have encountered something like “Media type application/vnd.oci.image.layer.v1.tar+zstd not supported”.
When all of that is already in place that’s great. If not, that’s a lot to do if you just want a quick test.
Many tutorials fail to mention that you can just run ready made images executing your compiled binaries directly. There are just 3 steps to run a dotnet application on a Linux Ubuntu server within an Arch Linux Docker container:
- Build your application binaries with dotnet publish
- Copy your binaries to your Linux server
- Set up and run your compose file (or create the container standalone)
1. Build your application
On your machine build your dotnet application to be used with an Arch Linux Base image:
dotnet publish -r linux-musl-x64 -c Release
You do not need a Dockerfile and no docker desktop. No modifications are required within your project. When using .net10 you will find the results in bin\Release\net10.0\linux-musl-x64\publish.
2. Copy your binaries to your server
On your Linux server prepare a directory to upload your binaries. In this example we create bin to hold the binaries. If you want to write out plain log files add a log directory, too. Your directory structure should look something like that:
/opt/stacks
└── /example-stack
├── docker-compose.yml
└── /app-v1
├── /bin
└── /logs
Copy your binaries from bin\Release\net10.0\linux-musl-x64\publish on your local machine to the bin directory on your Linux server. You can even use Visual Studio Code Remote Explorer to just drag and drop them. Make your binary executable (if you copy a newer version remember to do this again):
sudo chmod +x /opt/stacks/example-stack/app-v1/bin/sample-app
If you want to write out plain log files and you want to use a hardened base image (DHI image) you must adjust access permissions for your log directory:
sudo chown -R 65532:65532 /opt/stacks/example-stack/app-v1/logs
sudo chmod -R 775 /opt/stacks/example-stack/app-v1/logs
3. Set up and run
Add a normal container service to your Docker compose yaml file. Define the image, mount the binary directory, the log directory (optional) and add access to the machine certificates (required if you run an asp.net server and use a DHI image). You may add secrets, networks, dependencies and more.
services:
app-v1:
image: dhi.io/aspnetcore:10-alpine
working_dir: /app
volumes:
- ./app-v1/bin:/bin:ro
- ./app-v1/logs:/app/logs
- /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro
entrypoint: ["../bin/sample-app"]
Note: If you want to use a docker hardened image from dhi.io do not forget to define your docker.com login credentials. An account is free.
sudo docker login dhi.io -u <your_username>
To run your application, you can now deploy it the usual way:
docker stack deploy --compose-file /opt/stacks/example-stack/docker-compose.yml --with-registry-auth --prune <your_stack_name>
If this is your first time using docker stack remember to init the swarm first. Adjust the advertise-addr if needed.
sudo docker swarm init --advertise-addr 10.20.30.1
To update your application, you can simply copy your new binary (and libraries if they have changed) and update your running service:
docker service update --force <your-stack-name>_app-v1
When deploying a new version to our Component4 staging and testing servers the build process for the binaries takes around 3 seconds, while the whole deploying process is done in less than a minute. It allows us to quickly switch the base image, replace just a single dll or edit appsettings.json directly in place.
Another tip: To use docker secrets with your asp.net application you can use AddKeyPerFile in your Program.cs file:
var builder = WebApplication.CreateBuilder(args);
builder.AddKeyPerFile(directoryPath: "/run/secrets", optional: true);
If you add a load balancer (e.g. traefik) in front of your application you can have multiple versions of your application running at the same time (that’s why we add a v1 to versioned services) and delegate the request based on subdomain, request cookies, query parameters or http headers.
Top comments (0)