ce-infra/docs/incus/comparison-docker-vs-incus.md

167 lines
7.9 KiB
Markdown

# 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
```mermaid
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
```
```mermaid
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):
```mermaid
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:
1. Backup from Docker server
2. Deploy empty Incus server
3. Restore data into Incus containers
No data format conversion needed.