6 aug 2023
this is my summary of years working with nix/nixpkgs/nixcloud-webservices and docker.
i compare docker compose to nixcloud.webservices. we’ll also look briefly at kubernetes and nixcloud.containers which is LXC based.
in this context, a complex service would be a LAMP stack, an email service, like nixcloud.email or any ‘service’ which would require several docker containers, started simultaneously, to work.
a list of pros/cons to docker
and
docker compose
:
pro:
docker compose
deploymentsyaml
based, easy to learntraefik
reverse-proxy is easy to use (with let’s
encrypt ACME integration)con:
security running containers as root is bad, yet many do exactly that
no management node concept (controller in kubernetes)
it would be helpful if the deployment were controlled from a docker container itself - a controller node. the controller keeps the central configuration and offers this to the other nodes.
the .env file, the docker-compose.yaml and deployment specific files would reside on the controller and managed by it:
instead the deployment is controlled from the host, making deployment management platform dependent:
docker compose down
docker compose run --rm setup --domain=example.com --email=admin@example.com
docker compose up -d
stateful management tool
docker compose up
alone is not enough, users have to
pass explicit command line arguments:
incomplete APIs
docker compose
:
docker swarm
:
for a docker compose down
you need to be in the
right folder or you won’t be able to do this consistently
debugging restarting containers is hard
if container is in a restart loop, one cannot get a shell in it to access the logs what causes the issue
git platform specific document encoding: LF vs. LFCR
when working on Windows vs. working on Linux expect containers to fail in strange ways:
https://github.com/qknight/docker-ts5client
autostart would not work because the init script had ^M
https://github.com/matrix-org/synapse/issues/13691
bash: /start.py: /usr/local/bin/python^M: bad interpreter: No such file or directory
Dockerfile
: rebuilding images burns lots of
bandwidth and wastes download capacity
as apk add vim
or apt install vim
can’t
cache their downloads as done in nix/nixos
yaml syntax is very limited
yaml is no programming language and has very limited support to override configuration values compared to nixos options
shared volumes
-v ${PWD}/documents:/documents
UID/GID mapping of host to guest has platform specific differences
windows/mac blend host rights into the container but on linux files created inside the container often have root:root as owner and even worse, sometimes u+rw g-rwx o-rwx as permission.
entrypoint.sh not easily overridable
the concept of entrypoint.sh is amazing but when deriving a container with a Dockerfile the process to write a correct entrypoint.sh is hard and might easily degrade over time. it’d be great if one could have a mult-stage like environment for this.
no way to merge two different docker images
a workaround is multi-stage builds: copy programs from one image to another but this is only applicable for a few ‘selected’ binaries
docker desktop
i’ve been using docker desktop for windows for 18 months years now and though it is a remarkable help during development, my experience is that it is very early days with huge performance issues, memory leaks and the UI can be very lagging.
docker
and docker compose
is really useful
as it brings linux development tools onto windows. docker
brings reproducibility compared to manual setup of the environment but
not as complete as nix.
on the other hand, for the reasons listed above, docker deployment side lacks massively on all platforms supported.
my take for complex services:
docker
/ docker compose
for
your own service, using docker is fine. manual maintenance will be
required lots anywaythat said, single containers or simple deployments already bring huge benefits, see:
the docker
, docker compose
,
docker swarm
golang API bindings have been stagnant, as
pointed out above in parts. in contrast: kubernetes comes with this
popular library: https://github.com/kubernetes/client-go which features
the concept of a controller
which can be used to automate the deployment.
my advice for complex services:
using docker tooling you cannot automate docker deployment properly, as there are no APIs to do so, therefore i’d go for nix (not necessarily using containers).
nixos can be compared to docker compose
and nixops to
docker swarm
.
let’s start with a list of pros/cons to nix/nixos usage:
pro:
nix applications
nix tooling
the nix programming language is awesome! you can start with my tour of nix.
nix reproducibility and declarative configuration
see how easy it is to have your own email server abstraction: nixcloud.email
nixos options system is the administrators dream:
once the service is contributed into nixpkgs, you can use the option search system.
the best testing systems i’ve ever seen, see nixos tests.reproducibility it uses KVM VMs with a python abstraction to control VM states.
a derivation can be software as vim (binary, icons, scripts, …), or only a simple configuration file which is created on the fly from the nixos options system. docker uses the Dockerfile and each line this file generates a FS snapshot which is hashed. in nixos the Dockerfile concept matches to the configuration.nix.
i find the derivation concept hugely more powerful than the Dockerfile concept.
nixos container support
nixcloud.email - email server with 5 lines of code
nixcloud.TLS - this way TLS is handled outside of your application
nixcloud-container - LXC support
if you want to decouple the system nixos from the container nixos, you can use nixcloud-container in imperative mode, see https://github.com/nixcloud/nixcloud-container#declarative-vs-imperative-containers.
run your software in different environments
once your service can be built from nix you can put everywhere:
since nix has support for all of these.
nix-shell
nix-shell is a tool to create a shell environment from a nix expression. this is useful for development and testing.
nix-flake
nix-flakes are used to extend nixpkgs with third-party software, see https://github.com/nix-community/NUR as example.
flake documentation:
con:
nix expression programming
nixos tests
nixos tests are integration tests, see
won’t work with windows: nixos tests require KVM
nix package manager
won’t work with windows
works in WSL but the mix with native windows tools and WSL is complicated.
you are using nixos but the require software not in nixpkgs?
you want clion or goland which is hypothetically not abstracted in nixpkgs? in my experience this can range from an easy fix for nixpkgs or be a nightmare.
no IDE integration for language wrapper of nix
if you use clion for development, you will have to compile the source manually and once you change is final you will have to adapt the nix expression so also nix can build it.
if you use goland then dependency management for golang will have a different dependency resolution than the nix expression wrapping it.
https://discourse.nixos.org/t/nixos-bazel-and-clion-other-ide/1288/10
you need to build software for windows native
won’t work with windows but you might be able to setup a cross-compiler for it.
my advice for complex services:
or in a nutshell:
nix is great if you develop, deploy and manage the software yourself for a linux-like platform.
kubernetes works on windows, mac and linux and one can use the controller concept to automate the deployment of services.
say you build docker containers but you use kubernetes to deploy them, then you can use the controller concept to automate the deployment of your services.
my advice for complex services:
only go this path if you are really forced to as kubernetes has nice features but adds lots of burden. if you use ‘a managed kubernetes cluster’ this might result in huge costs.
note: by no means i’m an kubernetes expert.
my main question would be: “who or what is the controller your services”.
docker
the admin is the controller (no
automation)kubernetes
has a controller concept, so it can be
automated in golangnixos
the controller is likely implemented in the
nix language, so it can be automatedmy overall advice for complex services:
the nix strategy is an ‘all in’ solution and hugely powerful if the programming languages you use are supported by nixpkgs and if the colleagues rock.
hopefully this article sheds some light on the options and helps you to make a decision what stack to use.