Virtualization and ContainersDocker StacksIntermediate90 minutesLab

Build a Raspberry Pi Container Host with Docker Compose

Build a maintainable Raspberry Pi container host with Docker Engine, the current Compose plugin, service health checks, and a controlled update workflow.

Last reviewed8/18/2026
service update workflowscontainer health checkshomelab CI and rollback
Raspberry PiRaspberry Pi OS 64-bitDocker EngineDocker Compose pluginNginx

Expected Outcome

A 64-bit Raspberry Pi OS host running Docker Engine and the Compose plugin, with a small Nginx service that can be validated, updated deliberately, and backed out by restoring a known-good Compose definition and image reference.

Assumptions

  • Raspberry Pi 3, 4, or 5 running a current 64-bit Raspberry Pi OS release; verify `dpkg --print-architecture` returns `arm64` before using the Debian repository commands below.

  • SSH or local console access with a user that can run `sudo`.

  • Working DNS and internet access from the Raspberry Pi.

  • A maintenance window for package installation and container restarts.

Bill of Materials

  • Raspberry Pi with a reliable power supply and supported storage.

  • Current 64-bit Raspberry Pi OS installation.

  • Network connectivity to Docker's Debian package repository and Docker Hub.

  • A workstation or terminal for SSH access and a place to record validation and rollback evidence.

Build Steps

  1. Confirm the operating-system architecture

    Use the 64-bit path for a current long-lived build. Docker directs 64-bit Raspberry Pi OS users to the Debian arm64 packages. If this host reports armhf, stop and use Docker's Raspberry Pi OS 32-bit guidance instead; Docker Engine v28 is the final major release with official Raspberry Pi OS 32-bit packages.

    Read-only command: verify target and scope

    dpkg --print-architecture
    cat /etc/os-release
  2. Update the host and install repository prerequisites

    Bring the package index current and install the packages Docker uses to configure its signed apt repository. Review pending upgrades before applying them on an existing host.

    Changes system state: review before running

    sudo apt update
    apt list --upgradable
    sudo apt install -y ca-certificates curl
  3. Add Docker's official Debian apt repository

    Configure Docker's signed Debian repository rather than piping the convenience installer directly into a privileged shell. Docker documents the convenience script as a testing/development option; the repository path is easier to maintain and upgrade deliberately.

    Changes system state: review before running

    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    . /etc/os-release && printf 'Types: deb\nURIs: https://download.docker.com/linux/debian\nSuites: %s\nComponents: stable\nArchitectures: %s\nSigned-By: /etc/apt/keyrings/docker.asc\n' "$VERSION_CODENAME" "$(dpkg --print-architecture)" | sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null
    sudo apt update
  4. Install Docker Engine and the Compose plugin

    Install Docker Engine, containerd, Buildx, and Docker's current Compose plugin from the configured repository. The supported Linux command form is docker compose; the hyphenated docker-compose standalone path is legacy compatibility guidance.

    Changes system state: review before running

    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    sudo systemctl status docker --no-pager
    sudo docker compose version
    sudo docker run --rm hello-world
  5. Create a small Compose project

    Create a dedicated project directory and a Compose file. This example keeps the service intentionally small so that deployment, validation, and rollback behavior are easy to observe.

    Changes system state: review before running

    mkdir -p ~/my_docker_app
    cd ~/my_docker_app
    cat > compose.yaml <<'EOF'
    services:
      web:
        image: nginx:stable-alpine
        restart: unless-stopped
        ports:
          - "8080:80"
        healthcheck:
          test: ["CMD", "nginx", "-t"]
          interval: 30s
          timeout: 5s
          retries: 3
    EOF
    sudo docker compose config
  6. Deploy and validate the service

    Start the project, confirm Compose reports the service as running, and verify the published endpoint from the host. Do not treat a successful up command alone as proof that the application is healthy.

    Changes system state: review before running

    cd ~/my_docker_app
    sudo docker compose up -d
    sudo docker compose ps
    curl -fsS http://127.0.0.1:8080/ > /dev/null && echo 'HTTP validation passed'
  7. Use a controlled update workflow

    Record the currently deployed image reference and preserve the known-good Compose file before pulling a replacement. For a production-like rollback requirement, pin tested image digests instead of relying on a floating tag such as stable-alpine.

    Changes system state: review before running

    cd ~/my_docker_app
    cp compose.yaml compose.yaml.before-update
    sudo docker compose images
    sudo docker compose pull
    sudo docker compose up -d
    sudo docker compose ps
    curl -fsS http://127.0.0.1:8080/ > /dev/null && echo 'Post-update validation passed'

Validation

  • Confirm Docker Engine and Compose are installed from the expected path.

    Read-only command: verify target and scope

    sudo docker version
    sudo docker compose version
    Expected result: Both commands return version information without falling back to the legacy `docker-compose` executable.
  • Confirm the service is running and its health check is succeeding.

    Read-only command: verify target and scope

    cd ~/my_docker_app && sudo docker compose ps
    curl -fsS http://127.0.0.1:8080/ > /dev/null && echo 'Endpoint healthy'
    Expected result: The Compose service remains up and the host receives a successful HTTP response from port 8080.
  • Review recent service logs for restart loops or application errors before declaring the change complete.

    Read-only command: verify target and scope

    cd ~/my_docker_app && sudo docker compose logs --tail=100 web

Troubleshooting

  • If Docker does not start, inspect the service state and recent daemon logs before reinstalling packages.

    Read-only command: verify target and scope

    sudo systemctl status docker --no-pager
    sudo journalctl -u docker --since '-15 minutes' --no-pager
  • If the container starts but the endpoint fails, separate container state, health, port publishing, and application logs.

    Read-only command: verify target and scope

    cd ~/my_docker_app && sudo docker compose ps
    cd ~/my_docker_app && sudo docker compose logs --tail=100 web
    sudo ss -lntp | grep ':8080'
  • If you intentionally configure non-root Docker CLI access, follow Docker's Linux post-install guidance rather than hard-coding a username. Membership in the `docker` group grants root-level privileges.

Cleanup or Rollback

  • Stop and remove only the containers and network created by this project. This command does not remove named volumes unless you explicitly add `--volumes`.

    Changes system state: review before running

    cd ~/my_docker_app && sudo docker compose down
  • If an update fails and the previous Compose definition referenced a known-good immutable tag or digest, restore that definition and redeploy it.

    Warning: Restoring the Compose file alone is not a guaranteed image rollback when the file uses a mutable tag. Reliable rollback requires retaining or pinning the known-good image reference.

    Changes system state: review before running

    cd ~/my_docker_app && cp compose.yaml.before-update compose.yaml
    cd ~/my_docker_app && sudo docker compose up -d

Next Improvements

  • Replace the example floating image tag with a tested immutable image digest when reliable rollback is a requirement.

  • Add named volumes only after documenting which data must persist and how it will be backed up and restored.

  • Add centralized monitoring or log collection once the single-host deployment is stable.

References

Keep Moving

Build on what you just completed

Continue with a related Lab or return to this build path for a different implementation.