7.9 KiB
Docker vs Incus — Comparison for Cloud Elves Infrastructure
Executive Summary
Docker Compose is simpler for day-to-day operations and has a massive ecosystem. Incus offers stronger isolation, first-class backup/snapshot capabilities, and a unified platform for containers and VMs. For our current 4-person team on a single server, Docker is the pragmatic choice. Incus becomes compelling when we need VMs alongside containers, stronger tenant isolation for customer workloads, or a platform to offer as a product.
Architecture Comparison
How They Work
sequenceDiagram
participant Admin
participant Docker as Docker Compose
participant Containers as App Containers
Admin->>Docker: docker compose up
Docker->>Containers: Create all containers from YAML
Note over Docker: Declarative — single file defines entire stack
Docker-->>Admin: All services running
sequenceDiagram
participant Admin
participant Ansible
participant Incus
participant Containers as OCI Containers
Admin->>Ansible: ansible-playbook deploy.yml
Ansible->>Incus: incus launch (per service)
Incus->>Containers: Create containers individually
Ansible->>Incus: incus config device add (volumes, proxies)
Note over Ansible: Imperative — Ansible orchestrates step by step
Incus-->>Admin: All services running
Feature Comparison
| Feature | Docker Compose | Incus |
|---|---|---|
| Orchestration | Declarative YAML — one file defines everything | Imperative CLI — each container managed individually |
| Learning curve | Low — widely known, massive community | Medium — less common, different mental model |
| OCI image support | Native (this is Docker) | Supported since v6.3 (July 2024) |
| System containers | Not supported | First-class — full OS with systemd |
| Virtual machines | Not supported | First-class — run VMs alongside containers |
| DNS discovery | Compose service names on network | Container names on managed bridge (dnsmasq) |
| Port forwarding | ports: in compose file |
Proxy devices |
| Volumes | Named volumes | Storage pool volumes |
| Config mounts | Bind mounts | Disk devices |
| Network isolation | Multiple compose networks | Multiple bridges (but single bridge is simpler) |
| Restart policy | restart: unless-stopped |
boot.autostart=true (no crash restart) |
| Compose-like tooling | Docker Compose (mature) | incus-compose (incomplete/WIP) |
| Ecosystem | Enormous — Docker Hub, GitHub Actions, etc. | Growing — Linux Containers community |
Resource Overhead
| Scenario | Docker | Incus OCI | Incus System Container |
|---|---|---|---|
| Base container overhead | ~5-10MB | ~5-10MB (comparable) | ~50-100MB (full OS) |
| 9 containers (our stack) | ~100MB overhead | ~100MB overhead | ~500-900MB overhead |
| CPU overhead | Negligible | Negligible | Negligible |
| Disk per container | Layered images (efficient) | Converted images | Full rootfs (150-500MB) |
Bottom line: OCI containers in Incus have comparable overhead to Docker. System containers are heavier due to systemd and OS services.
Security & Isolation
| Aspect | Docker | Incus |
|---|---|---|
| Kernel sharing | Shared with host (same as Incus) | Shared with host (same as Docker) |
| Namespace isolation | Process, network, mount, IPC, UTS, user | Same namespaces + optional AppArmor/seccomp profiles |
| Root in container | Often runs as root (configurable) | System containers: full user space. OCI: same as Docker |
| Container escape risk | Kernel vulnerability = escape | Same risk for both |
| Multi-tenant isolation | Not designed for it | System containers provide stronger tenant boundaries |
| Docker socket exposure | Common pattern (e.g., Gitea runner) | Not needed — Incus has its own API |
Key insight: For our use case (single team, all trusted), the security difference is negligible. Incus's stronger isolation matters when hosting untrusted workloads or providing infrastructure to customers.
Operational Comparison
Day-to-Day Operations
| Task | Docker | Incus |
|---|---|---|
| Deploy all services | docker compose up -d |
ansible-playbook deploy.yml |
| View running services | docker compose ps |
incus list |
| View logs | docker compose logs gitea |
incus exec gitea -- cat /var/log/... or incus console gitea --type=log |
| Restart service | docker compose restart gitea |
incus restart gitea |
| Update service image | Edit compose, docker compose up -d |
incus stop gitea && incus delete gitea + relaunch |
| Enter container shell | docker compose exec gitea bash |
incus exec gitea -- bash |
| Deploy static site | rsync to host (unchanged) |
rsync to host (unchanged) |
Backup & Restore
| Capability | Docker | Incus |
|---|---|---|
| Database dump | docker compose exec db pg_dump |
incus exec db -- pg_dump |
| Volume backup | Manual: docker run --rm -v ... tar czf |
Built-in: incus storage volume export |
| Full instance snapshot | Not built-in | incus snapshot create (instant with ZFS) |
| Portable export | Manual tarball assembly | incus export (single command, includes volumes) |
| Remote backup | Needs external tool (Restic, etc.) | Same — snapshots are local only |
| Restore to different host | Manual — recreate containers, import data | incus import (single command) |
Incus advantage: Backup is a first-class feature. incus export creates a single portable file containing the container and all its data. With ZFS, snapshots are instant and space-efficient.
Service Updates
| Scenario | Docker | Incus |
|---|---|---|
| Bump image version | Edit compose file, up -d |
Delete container, relaunch with new image (data in volumes survives) |
| Config change | Edit template, up -d (smart restart) |
Edit template on host, incus restart |
| Rollback | Re-pull old image, up -d |
incus snapshot restore (if snapshot taken pre-upgrade) |
Docker advantage: Image updates are more ergonomic. Incus requires deleting and recreating the container (since OCI containers can't change their image in-place).
When to Choose Which
Choose Docker When
- Team is familiar with Docker (faster onboarding)
- Using many third-party services distributed as Docker images
- Want declarative stack definition (single compose file)
- Simple single-node deployment
- Ecosystem matters (GitHub Actions, CI/CD, monitoring tools all expect Docker)
Choose Incus When
- Need VMs alongside containers (e.g., testing, customer environments)
- Want first-class backup/snapshot capabilities
- Building infrastructure as a product for customers
- Need stronger multi-tenant isolation
- Want a single platform for containers + VMs + storage
- Comfortable with Ansible-driven infrastructure (no compose equivalent)
For Cloud Elves Specifically
Current recommendation: Stay with Docker for production. The stack is working, tested, and everyone knows it.
Build the Incus branch as a parallel capability for:
- Learning and R&D
- Potential product offering (Incus-managed customer environments)
- Testing whether Incus + ZFS backup model is simpler than Docker + Restic
- Evaluating system containers for future services that need full OS
Migration Path
Moving from Docker to Incus (if we decide to switch):
sequenceDiagram
participant Docker as Docker Server
participant Backup as Backup Storage
participant Incus as Incus Server
Docker->>Backup: Run backup.sh (pg_dumps + volume tarballs)
Note over Incus: Fresh Debian 13, Incus installed
Incus->>Incus: Run bootstrap.yml + deploy.yml
Backup->>Incus: Run restore.sh (import pg_dumps + volume data)
Note over Incus: All services running with restored data
The data formats are identical (Postgres dumps, file tarballs). The migration is just:
- Backup from Docker server
- Deploy empty Incus server
- Restore data into Incus containers
No data format conversion needed.