Container Runtime Guide

    Deploying containerd

    containerd is the industry-standard container runtime that powers Docker and Kubernetes. Deploy a minimal, focused runtime with maximum performance and minimal overhead on RamNode's reliable VPS hosting.

    Ubuntu 24.04 LTS
    containerd 1.7+
    nerdctl CLI
    20–30 min setup

    containerd vs Docker

    Docker is built on top of containerd. When you run docker run, Docker's CLI talks to the Docker daemon, which talks to containerd, which talks to runc. By running containerd directly, you eliminate the Docker daemon entirely.

    FeaturecontainerdDocker
    ArchitectureMinimal runtime (no daemon bloat)Client-server (dockerd daemon)
    Memory footprintLower (no dockerd process)Higher (~50–100 MB for dockerd)
    Container startsFaster (fewer abstraction layers)Slightly slower
    Attack surfaceSmaller (fewer running daemons)Larger
    KubernetesNative default runtimeRequires dockershim (deprecated)
    CLInerdctl (Docker-compatible) or ctrNative docker CLI

    Why RamNode for containerd

    • KVM virtualization with full kernel access for container operations
    • Starting at $4/month — 1 GB RAM works for lightweight workloads
    • $500 annual credit for new accounts
    • Generous bandwidth — image pulls won't be bottlenecked

    Prerequisites

    • A RamNode VPS running Ubuntu 24.04 LTS (1 GB for lightweight, 2 GB+ for production)
    • Root or sudo access
    • A domain name (optional, for exposing services)
    • Basic Linux command-line familiarity
    1

    Prepare Your RamNode VPS

    Update packages and install dependencies
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y \
      ca-certificates \
      curl \
      gnupg \
      lsb-release \
      apt-transport-https \
      software-properties-common

    Enable Required Kernel Modules

    containerd relies on specific kernel modules for networking and storage:

    Load kernel modules
    cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
    overlay
    br_netfilter
    EOF
    
    sudo modprobe overlay
    sudo modprobe br_netfilter

    Configure Kernel Networking Parameters

    Sysctl settings for container networking
    cat <<EOF | sudo tee /etc/sysctl.d/99-containerd.conf
    net.bridge.bridge-nf-call-iptables  = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    net.ipv4.ip_forward                 = 1
    EOF
    
    sudo sysctl --system
    Verify settings
    sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

    All three values should return 1.

    2

    Install containerd

    Option A: Docker's Official Repository (Recommended)

    Docker maintains the most up-to-date containerd packages:

    Install from Docker repository
    # Add Docker's GPG key
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
    
    # Add the repository
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    # Install containerd
    sudo apt update
    sudo apt install -y containerd.io

    Install runc

    runc is the low-level OCI runtime that containerd uses to create containers:

    Install runc
    RUNC_VERSION="1.2.4"
    
    curl -LO https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/runc.amd64
    sudo install -m 755 runc.amd64 /usr/local/sbin/runc

    Install CNI Plugins

    Container Networking Interface plugins handle network setup for containers:

    Install CNI plugins
    CNI_VERSION="1.6.1"
    
    curl -LO https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz
    sudo mkdir -p /opt/cni/bin
    sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v${CNI_VERSION}.tgz
    3

    Configure containerd

    Generate default config
    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml > /dev/null

    Enable SystemdCgroup

    Critical for proper resource management — without it, you'll encounter stability issues:

    Enable SystemdCgroup
    sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

    Key Config Settings

    /etc/containerd/config.toml (key sections)
    # Under [plugins."io.containerd.grpc.v1.cri"]
    # Set the sandbox (pause) image
    sandbox_image = "registry.k8s.io/pause:3.10"
    
    # Under [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
    SystemdCgroup = true
    
    # Under [plugins."io.containerd.grpc.v1.cri".registry]
    # Configure mirror endpoints if needed for faster pulls

    Apply Configuration

    Restart and verify
    sudo systemctl restart containerd
    sudo systemctl status containerd
    
    # Verify installation
    containerd --version
    runc --version

    You should see active (running) in the status output.

    4

    Install nerdctl & BuildKit

    ctr is containerd's native CLI but minimal. nerdctl provides a Docker-compatible interface that feels natural if you're coming from Docker.

    Install nerdctl
    NERDCTL_VERSION="2.0.3"
    
    curl -LO https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz
    sudo tar Cxzvf /usr/local/bin nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz

    Install BuildKit (for Building Images)

    containerd doesn't include a built-in image builder — BuildKit handles that:

    Install BuildKit
    BUILDKIT_VERSION="0.18.2"
    
    curl -LO https://github.com/moby/buildkit/releases/download/v${BUILDKIT_VERSION}/buildkit-v${BUILDKIT_VERSION}.linux-amd64.tar.gz
    sudo tar Cxzvf /usr/local buildkit-v${BUILDKIT_VERSION}.linux-amd64.tar.gz
    Create BuildKit systemd service
    cat <<EOF | sudo tee /etc/systemd/system/buildkit.service
    [Unit]
    Description=BuildKit
    Documentation=https://github.com/moby/buildkit
    After=containerd.service
    
    [Service]
    Type=notify
    ExecStart=/usr/local/bin/buildkitd --oci-worker=false --containerd-worker=true
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    sudo systemctl daemon-reload
    sudo systemctl enable --now buildkit

    Verify nerdctl Works

    Test with hello-world
    sudo nerdctl run --rm hello-world

    You should see the familiar "Hello from Docker!" message — but running through containerd directly.

    5

    Configure Networking with CNI

    Create a default bridge network configuration:

    CNI bridge configuration
    sudo mkdir -p /etc/cni/net.d
    
    cat <<EOF | sudo tee /etc/cni/net.d/10-containerd-bridge.conflist
    {
      "cniVersion": "1.0.0",
      "name": "containerd-bridge",
      "plugins": [
        {
          "type": "bridge",
          "bridge": "cni0",
          "isGateway": true,
          "ipMasq": true,
          "promiscMode": true,
          "ipam": {
            "type": "host-local",
            "ranges": [
              [{
                "subnet": "10.88.0.0/16"
              }]
            ],
            "routes": [
              { "dst": "0.0.0.0/0" }
            ]
          }
        },
        {
          "type": "firewall"
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
    EOF

    This gives containers a private 10.88.0.0/16 subnet with NAT for outbound traffic and port mapping for inbound access.

    6

    Rootless containerd (Optional)

    Running containers as a non-root user improves security — if a container is compromised, the attacker doesn't land as root on the host.

    Install prerequisites
    sudo apt install -y uidmap dbus-user-session

    Configure User Namespaces

    Create user and allocate UID/GID ranges
    # Replace 'deploy' with your non-root username
    USERNAME="deploy"
    
    sudo useradd -m -s /bin/bash $USERNAME 2>/dev/null || true
    
    # Allocate subordinate UID/GID ranges
    sudo usermod --add-subuids 100000-165535 $USERNAME
    sudo usermod --add-subgids 100000-165535 $USERNAME

    Install Rootless containerd

    Run as non-root user
    su - deploy
    containerd-rootless-setuptool.sh install

    📌 Note

    If the setup tool isn't available, configure it manually by setting up a user-level systemd service for containerd.

    7

    Resource Management & Limits

    On a VPS, resource management matters. containerd uses cgroups v2 (default on Ubuntu 24.04) for enforcement.

    Memory and CPU limits
    # Run a container with a 256MB memory limit
    sudo nerdctl run -d --name myapp --memory 256m --memory-swap 512m nginx:alpine
    
    # Limit to 0.5 CPU cores
    sudo nerdctl run -d --name myapp --cpus 0.5 nginx:alpine

    Monitor Resource Usage

    Resource monitoring
    # View real-time container stats
    sudo nerdctl stats
    
    # Inspect a specific container's resource consumption
    sudo nerdctl inspect myapp | grep -A 20 "Resources"

    Recommended Limits by Plan

    RamNode PlanRAMMax ContainersPer-Container Limit
    1 GB VPS1 GB1–2 lightweight128–256 MB each
    2 GB VPS2 GB3–4 lightweight256–512 MB each
    4 GB VPS4 GB6–8 mixed512 MB–1 GB each
    8 GB+ VPS8+ GB12+As needed
    8

    Log Management

    Containers can generate substantial logs. On a VPS, disk space is finite — configure log rotation early.

    System-level log rotation
    cat <<EOF | sudo tee /etc/logrotate.d/containerd
    /var/log/containerd/*.log {
        daily
        missingok
        rotate 7
        compress
        delaycompress
        notifempty
        create 0640 root root
    }
    EOF

    Limit journald Logs

    journald.conf settings
    # Edit /etc/systemd/journald.conf
    [Journal]
    SystemMaxUse=200M
    MaxFileSec=7day
    Apply journald changes
    sudo systemctl restart systemd-journald
    9

    Security Hardening

    AppArmor Profiles

    Ubuntu 24.04 ships with AppArmor enabled. Verify containerd uses it:

    Check AppArmor
    sudo aa-status | grep containerd

    Seccomp Profiles

    containerd applies a default seccomp profile that blocks dangerous syscalls:

    Verify seccomp
    sudo nerdctl inspect myapp | grep -i seccomp

    Firewall Configuration with UFW

    UFW rules
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow ssh
    sudo ufw allow 80/tcp    # HTTP
    sudo ufw allow 443/tcp   # HTTPS
    sudo ufw enable

    Restrict containerd Socket

    Socket permissions
    sudo chmod 660 /run/containerd/containerd.sock
    sudo chown root:root /run/containerd/containerd.sock
    10

    Common Operations Quick Reference

    nerdctl (Docker-Compatible CLI)

    nerdctl cheat sheet
    # Pull an image
    sudo nerdctl pull nginx:alpine
    
    # Run a container (detached, with port mapping)
    sudo nerdctl run -d --name webserver -p 80:80 nginx:alpine
    
    # List running containers
    sudo nerdctl ps
    
    # List all containers (including stopped)
    sudo nerdctl ps -a
    
    # View container logs
    sudo nerdctl logs webserver
    sudo nerdctl logs -f webserver    # Follow/stream logs
    
    # Execute a command inside a running container
    sudo nerdctl exec -it webserver /bin/sh
    
    # Stop and remove a container
    sudo nerdctl stop webserver
    sudo nerdctl rm webserver
    
    # Build an image from a Dockerfile
    sudo nerdctl build -t myapp:latest .
    
    # List images
    sudo nerdctl images
    
    # Remove unused images
    sudo nerdctl image prune -a
    
    # Docker Compose equivalent
    sudo nerdctl compose up -d
    sudo nerdctl compose down

    11

    Automated Backups

    Set up a cron job to snapshot your container volumes. This pairs well with RamNode's VPS snapshot capability.

    Backup script
    cat <<'EOF' | sudo tee /usr/local/bin/containerd-backup.sh
    #!/bin/bash
    BACKUP_DIR="/opt/backups/containerd"
    DATE=$(date +%Y%m%d-%H%M%S)
    
    mkdir -p "$BACKUP_DIR"
    
    # Export all running container images
    for container in $(sudo nerdctl ps -q); do
      NAME=$(sudo nerdctl inspect "$container" | grep -oP '"Name": "\K[^"]+' | head -1)
      IMAGE=$(sudo nerdctl inspect "$container" | grep -oP '"Image": "\K[^"]+' | head -1)
      sudo nerdctl save -o "$BACKUP_DIR/${NAME}-${DATE}.tar" "$IMAGE" 2>/dev/null
    done
    
    # Backup containerd config
    cp /etc/containerd/config.toml "$BACKUP_DIR/config.toml.${DATE}"
    
    # Clean up backups older than 7 days
    find "$BACKUP_DIR" -name "*.tar" -mtime +7 -delete
    find "$BACKUP_DIR" -name "config.toml.*" -mtime +7 -delete
    
    echo "[$(date)] Backup completed: $BACKUP_DIR"
    EOF
    
    sudo chmod +x /usr/local/bin/containerd-backup.sh
    
    # Run daily at 3 AM
    echo "0 3 * * * root /usr/local/bin/containerd-backup.sh >> /var/log/containerd-backup.log 2>&1" | \
      sudo tee /etc/cron.d/containerd-backup

    Troubleshooting

    IssueSolution
    containerd won't startCheck sudo journalctl -xeu containerd. Regenerate config if needed: containerd config default | sudo tee /etc/containerd/config.toml
    Containers can't reach the internetVerify sysctl net.ipv4.ip_forward returns 1 and check NAT rules with sudo iptables -t nat -L POSTROUTING -n
    Image pulls are slowConfigure a registry mirror in /etc/containerd/config.toml under registry.mirrors
    High memory usageCheck systemctl status containerd | grep Memory. Reduce concurrent operations or snapshot retention
    Invalid config.toml syntaxsudo rm /etc/containerd/config.toml && containerd config default | sudo tee /etc/containerd/config.toml
    Missing runc binaryReinstall runc: sudo install -m 755 runc.amd64 /usr/local/sbin/runc
    CNI config not loadingRestart containerd and verify config exists in /etc/cni/net.d/

    🎉 What's Next

    • Deploy a reverse proxy — set up Caddy or Traefik in a container for TLS termination and routing
    • Add container orchestration — K3s uses containerd as its default runtime and runs well on 2 GB+ VPS
    • Monitor with Prometheus — containerd exposes metrics at /v1/metrics for scraping
    • Set up CI/CD pipelines — use BuildKit's remote build capabilities for a container image build server