As a nix user myself, I wanted to create a deployment sytem that could be derived both as a nix package (my end goal for personal use) and as a set of docker images (for production use).
Being used to working in a reproducible environment with a nix-shell but aware that Docker was the industry standard, I did some research on the complementarity of both tools.
Docker ensures a consistant runtime environment. Once the image is built, you’re good to go. But if you need to rebuild it, you don’t have any guarantee you will be able to recreate it.
Nix on the other hand ensures a reproducible build. No matter when you retrieve the source, if you rebuilt it you will have the exact same result.
Is Docker still relevant then ? (was it worth it for me to learn it ? was my main question at first)
Docker has a formidable deployment ecosystem that ensure you can easily deploy your application anywhere. Moreover, most developer are used to using it and will be glad to keep a familiar setup
How ?
A) Creating a Docker image for my Kotlin backend
1) Creating a nix package
There seems to be multiple approach, one where you start from your raw code source (see Gradle2Nix) and one where you start with the jar.
I decided to keep it simple for now and went with the jar option.
I can now create a nix package by running
See the console output
The package is stored by default in my nix store, I can call it using
the hash is the signature of the package, being reproducible it will always be the same given the same input but any changes (to the config or the jar for exemple) will result in a distinct hash so that both version of the package can co-exist peacefully
The build command will also create a symlink locally in a result folder. I can call it using
and here we are, my package is running !
2) Creating a docker image
I now want to wrap my package in a docker container. Again, there are multiple ways to do it.
As before, I went with the easy route of just wrapping the build project inside a nix package
Again, I can run it as a standalone :
2) Creating a docker image
The docker container only needs to run the command and bim we’re done
D) Running them all together
I then use docker-compose to start them all so that they can communicate with each other
Note on the port, I opened a port to each container to allow independant access, but a member of the same network create by docker compose, they can adress each other trough any port using their name as the host (ex: agatha-db:5432, agatha-back:8000)
For example, the configuration file used by the backend to contact the database is like so :
Note I call the original port 5432, used by the service, not 4321 wich is the redirection I gave it in the docker compose
Everything is working smoothly and once I found the relevant tools, the code seems quite straight forward to improve and maintain.
I used dive to check on the images efficiency :
agatha-back :
Total Image size: 1.8GB <= why java ??
Potential wasted space: 0 B
Image efficiency score: 100 %
agatha-front :
Total Image size: 173 MB
Potential wasted space: 0 B
Image efficiency score: 100 %
agatha-db :
Total Image size: 419 MB
Potential wasted space: 8.3 MB
Image efficiency score: 98 % <= ironically the official image is the one with wasted space
I woudl like to improve it a bit in the future :
reduce the size of the java image (maybe by using a smaller jre)
take the migration process out of the docker image and in the nix package itself