How to Build Custom Container Images with Moby on Linux 2025

How to Build Custom Container Images with Moby on Linux 2025

The Moby Project provides the foundational components to build containerized systems without relying on the Docker daemon. If you're working on a project that needs fine-grained control over container image building or want to understand how Docker's container engine works under the hood, Moby offers modular, swappable components that let you assemble a custom container pipeline.

This guide walks you through building container images using Moby's toolkit on Linux, focusing on practical steps for developers who want to move beyond the standard Docker daemon.

Understanding Moby's Architecture

Moby is not a replacement for Docker itself—it's the upstream open-source project that Docker is built from. The key difference is philosophy: Moby provides "batteries included but swappable" components. You get container build tools, registries, runtimes, and orchestration pieces that you can mix and match.

The main components you'll interact with for image building are:

  • BuildKit: A modern build system for container images (more efficient than legacy builders)
  • containerd: A lightweight container runtime specification-compliant implementation
  • runc: A low-level container runtime that manages the actual container processes

Prerequisites

Before starting, ensure you have:

  • A Linux system (Ubuntu 20.04 LTS or later recommended)
  • Root or sudo access
  • Basic understanding of Docker/container concepts
  • Go 1.20 or later (if compiling from source)
  • git installed

Step 1: Install containerd and runc

The foundation of Moby is containerd, the container runtime. Install it on your Linux system:

# Download the latest containerd release
wget https://github.com/containerd/containerd/releases/download/v1.7.18/containerd-1.7.18-linux-amd64.tar.gz

# Extract to /usr/local
sudo tar -C /usr/local -xzf containerd-1.7.18-linux-amd64.tar.gz

# Create systemd service
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

# Start containerd
sudo systemctl start containerd
sudo systemctl enable containerd

Verify installation:

containerd --version
# Output: containerd github.com/containerd/containerd v1.7.18 ...

Next, install runc (the OCI runtime):

wget https://github.com/opencontainers/runc/releases/download/v1.1.12/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
runc --version

Step 2: Set Up BuildKit

BuildKit is Moby's modern build system. It's more efficient and feature-rich than the legacy builder:

# Download BuildKit
wget https://github.com/moby/buildkit/releases/download/v0.12.5/buildkit-v0.12.5.linux-amd64.tar.gz

# Extract
sudo tar -C /usr/local -xzf buildkit-v0.12.5.linux-amd64.tar.gz

# Verify
buildkitd --version

Start the BuildKit daemon:

# Run as background service
sudo buildkitd &

# Or use systemd (create /etc/systemd/system/buildkit.service)
[Unit]
Description=BuildKit
After=network.target

[Service]
Type=notify
ExecStart=/usr/local/bin/buildkitd
Restart=always

[Install]
WantedBy=multi-user.target

Step 3: Create a Simple Dockerfile

Create a test Dockerfile to build:

# Dockerfile
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    curl \
    git \
    build-essential

WORKDIR /app

COPY . .

RUN echo "Built with Moby BuildKit"

CMD ["/bin/bash"]

Step 4: Build the Image with BuildKit

Use buildctl (the BuildKit CLI) to build:

# Build image and load into containerd
buildctl build \
  --frontend=dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=image,name=localhost/my-app:latest,push=false

For a more practical workflow, you can also use containerd directly with BuildKit:

buildctl build \
  --frontend=dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=oci,dest=/tmp/image.tar

Then import into containerd:

ctr image import /tmp/image.tar

Step 5: Run the Container

Once built, run it with containerd's ctr command:

# List images
ctr image ls

# Run container
ctr run --rm localhost/my-app:latest my-container /bin/bash

Or use nerdctl (a Docker-like CLI for containerd):

# Install nerdctl
wget https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gz
sudo tar -C /usr/local -xzf nerdctl-1.7.6-linux-amd64.tar.gz

# Use familiar Docker-like commands
nerdctl run --rm localhost/my-app:latest /bin/bash

Moby vs Standard Docker: Key Differences

| Aspect | Moby | Docker Desktop | |--------|------|----------------| | Target Audience | Developers, integrators, hackers | End users, enterprises | | Support | Community, best-effort | Commercial support available | | Modularity | Swappable components | Integrated stack | | Learning Curve | Steeper (manual setup) | Gentle (pre-configured) | | Customization | High (build your own stack) | Limited (Docker's way) | | Use Case | Custom container systems, edge computing | Development, testing, deployment |

Common Pitfalls and Solutions

Issue: BuildKit daemon won't start

Ensure containerd is running first, and check logs:

sudo journalctl -u buildkit -n 50

Issue: Permission denied errors

Add your user to the containerd group:

sudo usermod -aG containerd $USER
newgrp containerd

Issue: Image not appearing in ctr image ls

Verify the image was actually imported:

ctr image list --quiet

Why Choose Moby?

Moby excels when you need:

  • Custom container runtimes for embedded systems or Kubernetes nodes
  • Lightweight setups without full Docker Desktop overhead
  • Understanding of container fundamentals by working with modular pieces
  • Integration into larger systems like CI/CD pipelines or edge devices

The Moby Project's philosophy aligns with developers who want to understand how containers work and build systems tailored to their specific requirements, rather than accepting Docker's opinionated stack as-is.

Next Steps

Now that you have a working Moby build pipeline, explore:

  • BuildKit advanced features: Caching strategies, multi-stage builds
  • Kubernetes integration: Running containerd as the runtime for K8s
  • Registry setup: Hosting your own image registry with Moby components
  • Custom runtimes: Experimenting with alternative OCI runtimes like crun or kata

The Moby Project's modular architecture means each component can be swapped and upgraded independently, giving you flexibility that Docker Desktop's monolithic approach doesn't offer.

Recommended Tools

  • DockerDevelop faster. Run anywhere.
  • DigitalOceanCloud hosting built for developers — $200 free credit for new users