Skip to main content
The Docker bundlers transform Nix derivations into Docker images using nixpkgs’ dockerTools. They create minimal, layered images optimized for fast pulls and efficient caching.

Available Bundlers

Bundler NameOutput FormatUse Case
docker.tar.gz archiveSave to file, load later, or distribute
docker-streamStreamed outputPipe directly to docker load

How They Work

Both bundlers use dockerTools.buildLayeredImage (or streamLayeredImage) to:
  1. Layer the image - Separate dependencies into layers for efficient caching
  2. Include CA certificates - Add dockerTools.caCertificates for HTTPS support
  3. Set the command - Configure the container to run your application by default
  4. Tag the image - Use derivation name and version for the image tag

Implementation

From bundlers/docker/default.nix:5-17:
pkgs.dockerTools.buildLayeredImage {
  name = "${drv.pname}";
  tag = "${drv.version}";
  created = "now";
  meta = drv.meta;
  contents = with pkgs; [
    dockerTools.caCertificates
    drv
  ];
  config.Cmd = [
    "${pkgs.lib.meta.getExe drv}"
  ];
}

Basic Usage

docker Bundler

Creates a .tar.gz archive:
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker \
  .#myapp
Result: /nix/store/...-docker-image-myapp.tar.gz Load into Docker:
docker load < /nix/store/...-docker-image-myapp.tar.gz

docker-stream Bundler

Streams directly to Docker:
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp | docker load
Result: Image loaded directly into Docker daemon

Complete Workflow

Build and Run

1

Bundle the application

nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp | docker load
2

Verify the image

docker images | grep myapp
Output:
myapp    1.0.0    abc123def456    5 seconds ago    25.3MB
3

Run the container

docker run --rm myapp:1.0.0

Save and Distribute

Create a distributable archive:
# Build the image
IMAGE=$(nix bundle \
  --bundler github:nurpkgs/nur-nix#docker \
  .#myapp)

# Copy to current directory
cp "$IMAGE" ./myapp-docker.tar.gz

# Distribute the archive
# Users can load it with:
# docker load < myapp-docker.tar.gz

Image Configuration

Default Command

The bundler automatically sets the container’s default command to your application’s main executable:
config.Cmd = [
  "${pkgs.lib.meta.getExe drv}"
];
Override at runtime:
docker run --rm myapp:1.0.0 /bin/sh

Included Contents

Each image includes:
  • Your application derivation
  • CA certificates for HTTPS/TLS
  • Minimal dependencies (layered for efficiency)
Images are minimal by design. They don’t include a shell or common utilities unless your derivation depends on them.

Advanced Usage

Custom Image Configuration

Create a custom bundler with additional configuration:
{
  description = "Custom Docker bundler";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in
    {
      bundlers.${system}.docker-custom = drv:
        pkgs.dockerTools.buildLayeredImage {
          name = "${drv.pname}";
          tag = "${drv.version}";
          created = "now";
          
          contents = with pkgs; [
            dockerTools.caCertificates
            drv
            bash  # Add shell
            coreutils  # Add common utilities
          ];
          
          config = {
            Cmd = [ "${pkgs.lib.meta.getExe drv}" ];
            Env = [
              "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
            ];
            ExposedPorts = {
              "8080/tcp" = {};
            };
            WorkingDir = "/app";
          };
        };
    };
}

Multi-Architecture Images

Build images for different architectures:
# Build for x86_64 Linux
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker \
  --system x86_64-linux \
  .#myapp

# Build for ARM64 Linux
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker \
  --system aarch64-linux \
  .#myapp
Cross-architecture builds may require QEMU or platform-specific builders configured in your Nix setup.

Image Layers

The buildLayeredImage function automatically creates layers:
  • Base layer: Common dependencies
  • Dependency layers: Separated by dependency tree
  • Application layer: Your application code
Benefits:
  • Faster pulls (only changed layers downloaded)
  • Better caching (unchanged layers reused)
  • Smaller total transfer size across multiple images

Comparison: docker vs docker-stream

Featuredockerdocker-stream
Output.tar.gz filestdout stream
StorageSaved to diskNo intermediate file
Use caseDistribute, save for laterImmediate loading
PerformanceSlower (disk I/O)Faster (no disk write)
ShareableYesNo

When to Use Each

Use docker:
  • Building for distribution
  • Saving images for CI/CD artifacts
  • Archiving images for offline use
  • Sharing images via file transfer
Use docker-stream:
  • Local development
  • Immediate testing
  • CI/CD pipelines (when not archiving)
  • Memory-constrained environments

Practical Examples

CI/CD Pipeline

# .github/workflows/docker.yml
name: Build Docker Image

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: cachix/install-nix-action@v20
      
      - name: Build Docker image
        run: |
          nix bundle \
            --bundler github:nurpkgs/nur-nix#docker-stream \
            .#myapp | docker load
      
      - name: Test image
        run: docker run --rm myapp:1.0.0 --version
      
      - name: Push to registry
        run: |
          docker tag myapp:1.0.0 ghcr.io/myorg/myapp:1.0.0
          docker push ghcr.io/myorg/myapp:1.0.0

Local Development

#!/usr/bin/env bash
set -e

echo "Building Docker image..."
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp | docker load

echo "Running container..."
docker run --rm -p 8080:8080 myapp:1.0.0

Multi-Stage Build Alternative

Instead of multi-stage Dockerfiles, use Nix:
# flake.nix
{
  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in
    {
      packages.${system}.myapp = pkgs.buildGoModule {
        name = "myapp";
        src = ./.;
        vendorHash = "sha256-...";
      };
    };
}
# Single command replaces entire multi-stage Dockerfile
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp | docker load

Troubleshooting

Image Not Found After Load

docker images
# myapp not listed
Solution: Check the image was loaded successfully:
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp 2>&1 | docker load

Permission Denied

Cannot connect to the Docker daemon
Solution: Ensure Docker daemon is running and you have permissions:
sudo usermod -aG docker $USER
newgrp docker

Large Image Size

Images are larger than expected. Solution: Check what’s included:
# Load image
nix bundle \
  --bundler github:nurpkgs/nur-nix#docker-stream \
  .#myapp | docker load

# Inspect layers
docker history myapp:1.0.0
Nix automatically includes only required dependencies. Large images usually mean your application has large dependencies.

Deno Bundlers

Bundle before containerizing

Go Bundlers

Smaller Go binaries for containers

Build docs developers (and LLMs) love