Docker

From NixOS Wiki
Revision as of 11:16, 15 December 2024 by Onny (talk | contribs) (Merge Docker/Podman documentation in here)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Docker is a utility to pack, ship and run any application as a lightweight container.

Setup

To install docker, add the following to your NixOS configuration:

virtualisation.docker.enable = true;

More options are available.

To get access to the docker socket, you have to be in the docker group:

Warning: Beware that the docker group membership is effectively equivalent to being root!
Consider using rootless mode below.
users.users.<myuser>.extraGroups = [ "docker" ];

After changing the group, a reboot or re-login might be required.

Docker on btrfs

Note: If you use the btrfs file system, you might need to set the storageDriver option:

virtualisation.docker.storageDriver = "btrfs";

Rootless docker

To use docker in rootless mode, you can activate the rootless option:

virtualisation.docker.rootless = {
  enable = true;
  setSocketVariable = true;
};

The setSocketVariable option sets the DOCKER_HOST variable to the rootless Docker instance for normal users by default.

Changing Docker Daemon's Data Root

By default, the Docker daemon will store images, containers, and build context on the root file system.

If you want to change the location that Docker stores its data, you can configure a new data-root for the daemon by setting the data-root property of the virtualisation.docker.daemon.settings.

virtualisation.docker.daemon.settings = {
  data-root = "/some-place/to-store-the-docker-data";
};

Docker Containers as systemd Services

To make sure some docker containers are running as systemd services, you can use oci-containers:

virtualisation.oci-containers = {
  backend = "docker";
  containers = {
    foo = {
      # ...
    };
  };
};

A more advanced example

{ config, pkgs, ... }:

{
  config.virtualisation.oci-containers.containers = {
    hackagecompare = {
      image = "chrissound/hackagecomparestats-webserver:latest";
      ports = ["127.0.0.1:3010:3010"];
      volumes = [
        "/root/hackagecompare/packageStatistics.json:/root/hackagecompare/packageStatistics.json"
      ];
      cmd = [
        "--base-url"
        "\"/hackagecompare\""
      ];
    };
  };
}

See oci-containers for further options.

Usage

NixOS uses Podman to run OCI containers. Note that these are user-specific, so running commands with or without sudo can change your output.

List containers

# podman ps

Update image

# podman restart hackagecompare

List images

# podman ls

Remove container

# podman rm hackagecompare

Remove image

# podman rmi c0d9a5f58afe

Update image

# podman pull chrissound/hackagecomparestats-webserver:latest

Run interactive shell in running container

# podman exec -ti $ContainerId /bin/sh

Creating images with Nix

Building a docker image with nixpkgs

There is an entry for dockerTools in the Nixpkgs manual for reference. In the linked page, they give the following example config:

buildImage {
  name = "redis";
  tag = "latest";

  fromImage = someBaseImage;
  fromImageName = null;
  fromImageTag = "latest";

  copyToRoot = pkgs.buildEnv {
    name = "image-root";
    paths = [ pkgs.redis ];
    pathsToLink = [ "/bin" ];
  };

  runAsRoot = ''
    #!${pkgs.runtimeShell}
    mkdir -p /data
  '';

  config = {
    Cmd = [ "/bin/redis-server" ];
    WorkingDir = "/data";
    Volumes = { "/data" = { }; };
  };

  diskSize = 1024;
  buildVMMemorySize = 512;
}

More examples can be found in the nixpkgs repo.

Also check out the excellent article by lethalman about building minimal docker images with nix.

Reproducible image dates

The manual advises against using created = "now", as that prevents images from being reproducible.

An alternative, if using flakes, is to do created = builtins.substring 0 8 self.lastModifiedDate, which uses the commit date, and is therefore reproducible.

How to calculate the sha256 of a pulled image

The sha256 argument of the dockerTools.pullImage function is the checksum of the archive generated by Skopeo. Since the archive contains the name and the tag of the image, Skopeo arguments used to fetch the image have to be identical to those used by the dockerTools.pullImage function.

For instance, the SHA of the following image

pkgs.dockerTools.pullImage{
  imageName = "lnl7/nix";
  finalImageTag = "2.0";
  imageDigest = "sha256:632268d5fd9ca87169c65353db99be8b4e2eb41833b626e09688f484222e860f";
  sha256 = "1x00ks05cz89k3wc460i03iyyjr7wlr28krk7znavfy2qx5a0hfd";
};

can be manually generated with the following shell commands

skopeo copy docker://lnl7/nix@sha256:632268d5fd9ca87169c65353db99be8b4e2eb41833b626e09688f484222e860f docker-archive:///tmp/image.tgz:lnl7/nix:2.0
nix-hash --base32 --flat --type sha256 /tmp/image.tgz
1x00ks05cz89k3wc460i03iyyjr7wlr28krk7znavfy2qx5a0hfd

Directly Using Nix in Image Layers

Instead of copying Nix packages into Docker image layers, Docker can be configured to directly utilize the nix-store by integrating with nix-snapshotter.

This will significantly reduce data duplication and the time it takes to pull images.

Docker Compose

Currently, there are two options to use Docker Compose with NixOS: Arion or Compose2Nix.

With Arion, you can specify most Docker Compose options in Nix Syntax, and Arion will generate a docker-compose.yml file internally. The result is a systemd service that starts and stops the container.

Compose2Nix, generates all necessary configs directly from the docker-compose.yml, which is easier when using an already existing Docker Compose project. The result is similar to that from Arion: a systemd service is created that handles starting and stopping the container.

Arion

Arion is created for running Nix-based projects in Docker Compose. It uses the NixOS module system for configuration, it can bypass docker build and lets you use dockerTools or use the store directly in the containers. The images/containers can be typical dockerTools style images or full NixOS configs.

To use Arion, you first need to add its module to your NixOS configuration:

modules = [ arion.nixosModules.arion ];

After that, you can access its options under

virtualisation.arion = {}

A config for a simple container could look like this:

virtualisation.arion = {
  backend = "docker";
  projects = {
    "db".settings.services."db".service = {
      image = "";
      restart = "unless-stopped";
      environment = { POSTGRESS_PASSWORD = "password"; };
    };
  };
};

Compose2Nix

With compose2nix you can generate oci-containers config from a docker-compose.yaml.

Install

To use compose2nix with nix-shell you can use

nix shell github:aksiksi/compose2nix
compose2nix -h

To install compose2nix to NixOS, add the repo to your flake inputs

compose2nix = {
  url = "github:aksiksi/compose2nix";
  inputs.nixpkgs.follows = "nixpkgs";
};

and add the package to your configuration

environment.systemPackages = [
  inputs.compose2nix.packages.x86_64-linux.default
];
Usage

After you have installed compose2nix, you can run compose2nix in the directory with your docker-compose.yml, which will output a docker-compose.nix.

Alternatively, you can specify the input and output files with the following flags

compose2nix -inputs input.yml -output output.nix -runtime docker

The -runtime flag specifies the runtime. Here, we select docker. Options are podman and docker. The default is podman

Using Nix in containers

While dockerTools allows to build lightweight containers, it requires nix to be installed on the host system. An alternative are docker images with nix preinstalled:

NixOS can be run in containers using Arion.

Troubleshooting

Cannot connect to public Wi-Fi, when using Docker

When connecting to a public Wi-Fi, where the login page's IP-Address is within the Docker network range, accessing the Internet might not be possible. This has been reported when trying to connect to the WIFIonICE of the Deutsche Bahn (DB). They use the 172.18.x.x address range.

This can be resolved by changing the default address pool that Docker uses.

virtualisation.docker = {
  enable = true;
  daemon.settings = {
    "default-address-pools" = [
      { "base" = "172.27.0.0/16"; "size" = 24; }
    ];
  };
};

Restarting, the container or Docker might be required.

See also

Workgroup:Container

Alternatively you can use podman.