Use nix + docker for development

I have a very basic usage - launch a postgres docker with nix env, but I do not find corresponded docs on the internet. Also searching here with keywords like nix and docker returns nixos in docker that kind of questions or cache nix store in gitlab ci. Those are different from what I am after. So here is my question; and please correct me pointing to the doc that I should read first. I appreciate it.

My use case is to create an env in nix where I can launch postgres docker because later on I may have multiple different nix envs with corresponded postgres docker instances running at the same time on the same machine for different purposes.

The doc I follow is this one[1]. I can create nix env with following nix content, and can confirm there exists postgres command.

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
    buildInputs = [

However, when trying the second step nixos-container create foo --config-file postgres-config.nix, nix env complains

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = "en_US:en",
    LC_ALL = (unset),
    LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
write_file '/etc/containers/foo.conf' - sysopen: Permission denied at /nix/store/jpx4hfriwdycw5rn7prblsf8mnk8g97f-nixos-container/bin/nixos-container line 250.

where postgres-config.nix uses the exact same config file mentioned in the doc as below

{ pkgs, ... }:
    system.stateVersion = "20.09";
    networking.firewall.allowedTCPPorts = [ 5432 ];
    services.postgresql = {
        enable = true;
        enableTCPIP = true;
        extraPlugins = with pkgs.postgresql.pkgs; [ postgis ];
        authentication = "host all all trust";
        ensureDatabases = [ "foo" ];
        ensureUsers = [{
            name = "foo";
            ensurePermissions."DATABASE foo" = "ALL PRIVILEGES";

If I run nixos-container with sudo, it will complains sudo: nixos-container: command not found because there is no nixos-container for root, and I do not want to use root to do that as well.

How can I set LOCALE for nix postgres env and how to prevent nixos-container write postgres config file foo.conf to /etc? Or what is the correct way to do that?

I appreciate any suggestions and commentary, thanks!

[1]. - Setting up a dev environment for PostgreSQL with nixos-container

1 Like
  1. Use configuration.nix to define containers via containers with the various versions of postgres (and anything else you might need).

  2. Set autoStart = false; on them to ensure they don’t step on each others toes.

  3. As and when needed, you just do ‘sudo nixos-container start container_name’

If instead you want to use containers created by other people, I suggest looking into podman as that will run rootlessly.


Note that nixos containers are not docker containers. You may be interested in the dockertools section of the nixpkgs manual, as well as virtualisation.oci-containers.containers.

The documentation for nixos containers lives in the nixos manual.

All three of these options (docker, podman, nixos-containers) use cgroups. nixos-containers are useful because you can just use nix modules in them, but networking is very painful to configure if you want to connect containers together. I’m partial to podman when it comes to oci containers, for the reason @peterhoeg mentions.


nixos containers are actually systemd nspawn containers…!

You can have your nixos containers use host networking if you don’t need multiple instances of the same thing running at the same time and thus fight over ports. It’s much easier as it works as if the various services are just running directly outside a container.

Every now and then I regretfully have to touch some older PHP applications, and having all the various requirements in a container that launches in about a second and configured via the NixOS module system is the only pleasant thing about that experience.

Yep, getting the basic networking sorted is fine :slight_smile: It’s when you want to do fancy container linking, with connections between containers that other containers can’t see, that you run into the use cases for podman/docker.

It’s when you want to do fancy container linking, with connections between containers that other containers can’t see, that you run into the use cases for podman/docker.

I don’t have a need for any complicated setups, but podman is very nice whenever you need to run anything that’s being distributed as an OCI image and of course for testing OCI images created by dockerTools that is expected to run somewhere else.

I am completely not aware of it at all, thanks!

It looks like what I originally posted is different from my expectation. So I try again based on the docs [1], [2], and [3] as suggested. The .nix file I used now is following (the .nix file name is postgres-docker.nix).

{ pkgs ? import <nixpkgs> { }}: # , buildImage, buildLayeredImage, fakeNss, pullImage, shadowSetup, buildImageWithNixDb, pkgsCross }:

rec {
  postgresFromDockerHub = pkgs.dockerTools.pullImage {
    imageName = "postgres";
    imageDigest = "sha256:1cb07b811f2ec7b7bf3c42fc3dc19c1b5e87775eee88c2b1a8793420c4de0781";
    # The corrected sha256 value should be 0d18sz2b91mzxhbc2pkfjgck36fxq9113rw5c8z0x7ank4aby2f8 instead
    sha256 = "0warqbxmxmc0f1qr8pfsfq8006dyvrcfpjqyxvnblpqgnilkcvcn"; 
    finalImageTag = "latest";
    finalImageName = "postgres-latest";

  postgres = pkgs.dockerTools.buildImage {
    name = "postgres-docker";
    tag = "latest";
    fromImage = postgresFromDockerHub;
    fromImageName = null;
    fromImageTag = "latest";
    # contents = pkgs.postgres;
    runAsRoot = ''
      mkdir -p /data

    config = {
      Cmd = [ "postgres" ];
      WorkingDir = "/data";
      Volumes = { "/data" = { }; };

With that .nix file, it looks like I can create and load docker image

$ nix-build postgres-docker.nix
Cooking the image...

$ docker load < result
1b1478b68171: Loading layer  15.36kB/15.36kB
f5e4ed210274: Loading layer  10.24kB/10.24kB
Loaded image: postgres-docker:latest

There is an additional issue:

  • I notice that I need to provide sha256 value; however I completely have no idea how it’s calculated. Though I copy the one calculated by nix and it works. But how do I know or calculate that sha25 as verification?

  • Also it produces two result (result and result-2) files. Why there are two result files? Is one for pullImage (as base image) while the other buildImage (the docker image to run via docker run -t ${repository}:${tag})?
    lrwxrwxrwx … result-2 → /nix/store/1czb3mpd60ssxf8clgljfhiiihnx0wb1-docker-image-postgres-latest-latest.tar
    lrwxrwxrwx … result → /nix/store/bivalmw6qqxzzyc702w7vvhh7zbxmpf1-docker-image-postgres-docker.tar.gz


[1]. NixOS - Nixpkgs 21.11 manual
[2]. NixOS - Nixpkgs 21.11 manual
[3]. nixpkgs/examples.nix at e9c2918e9bce73019f9581b26555bbd5bcb44e78 · NixOS/nixpkgs · GitHub
[4]. nix complains "error: cannot auto-call a function that has an argument without a default value ('stdenv')"? · GitHub

1 Like

It’s documented with pullImage; it’s just sha256sum of the whole image. Docker images are basically zip files, docker pull just normally downloads them to a somewhat hidden directory so you don’t realize.

Nix will use the same API to download them from dockerhub. You could figure out what the URL is and download manually, use docker export, or simply use nix-prefetch-docker, which does exactly what you would (download image, checksum it, write expression).

In either case you’re trusting that dockerhub is safe (and nobody has stolen an account of an image owner), and that the https connection is safe (and nobody has stolen a root certificate, or has a long-term MITM over your connection). Neither are totally safe assumptions, but there’s not really a better approach for knowing what the checksum should be, besides building the docker images from scratch, and you’re already relying on the public nix cache/github, so trying to go for more security than nix-prefetch-docker here is a bit pointless.

Note that your image built using pkgs.dockerTools.buildImage is superfluous. The other function already creates an image that does exactly the same thing, though that is the way to make small modifications if they’re necessary.

1 Like

That’s lots of info. Thanks for the input. I need to spend some time looking into it. Thank you!