Files
Gondulf/docs/decisions/ADR-009-podman-container-engine-support.md
Phil Skentelbery 01dcaba86b feat(deploy): merge Phase 5a deployment configuration
Complete containerized deployment system with Docker/Podman support.

Key features:
- Multi-stage Dockerfile with Python 3.11-slim base
- Docker Compose configurations for production and development
- Nginx reverse proxy with security headers and rate limiting
- Systemd service units for Docker, Podman, and docker-compose
- Backup/restore scripts with integrity verification
- Podman compatibility (ADR-009)

All tests pass including Podman verification testing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 19:16:54 -07:00

8.2 KiB

ADR-009: Podman as Primary Container Engine

Date: 2025-11-20

Status

Accepted

Context

The Phase 5a deployment configuration was initially designed with Docker as the primary container engine. However, Podman has emerged as a compelling alternative with several security and operational advantages:

Podman Advantages:

  • Daemonless Architecture: No background daemon required, reducing attack surface and resource overhead
  • Rootless by Default: Containers run without root privileges, significantly improving security posture
  • OCI-Compliant: Adheres to Open Container Initiative standards for maximum compatibility
  • Pod Support: Native pod abstraction (similar to Kubernetes) for logical container grouping
  • Docker-Compatible: Drop-in replacement for most Docker commands
  • systemd Integration: Native support for generating systemd units for production deployments

Key Technical Differences Requiring Design Consideration:

  1. UID Mapping: Rootless containers map UIDs differently than Docker

    • Container UID 1000 maps to host user's subuid range
    • Volume permissions require different handling
  2. Networking: Different default network configuration

    • No docker0 bridge
    • Uses slirp4netns or netavark for rootless networking
    • Port binding below 1024 requires special configuration in rootless mode
  3. Compose Compatibility: podman-compose provides Docker Compose compatibility

    • Not 100% feature-parity with docker-compose
    • Some edge cases require workarounds
  4. Volume Permissions: Rootless mode has different SELinux and permission behaviors

    • May require :Z or :z labels on volume mounts (SELinux)
    • File ownership considerations in bind mounts
  5. systemd Integration: Podman can generate systemd service units

    • Better integration with system service management
    • Auto-start on boot without additional configuration

Decision

We will support Podman as the primary container engine for Gondulf deployment, while maintaining Docker compatibility as an alternative.

Specific Design Decisions:

  1. Container Images: Build OCI-compliant images that work with both podman and docker
  2. Compose Files: Provide compose files compatible with both podman-compose and docker-compose
  3. Volume Mounts: Use named volumes by default to avoid rootless permission issues
  4. Documentation: Provide parallel command examples for both podman and docker
  5. systemd Integration: Provide systemd unit generation for production deployments
  6. User Guidance: Document rootless mode as the recommended approach
  7. SELinux Support: Include :Z/:z labels where appropriate for SELinux systems

Consequences

Benefits

  1. Enhanced Security: Rootless containers significantly reduce attack surface
  2. No Daemon: Eliminates daemon as single point of failure and attack vector
  3. Better Resource Usage: No background daemon consuming resources
  4. Standard Compliance: OCI compliance ensures future compatibility
  5. Production Ready: systemd integration provides enterprise-grade service management
  6. User Choice: Supporting both engines gives operators flexibility

Challenges

  1. Documentation Complexity: Must document two command syntaxes
  2. Testing Burden: Must test with both podman and docker
  3. Feature Parity: Some docker-compose features may not work identically in podman-compose
  4. Learning Curve: Operators familiar with Docker must learn rootless considerations
  5. SELinux Complexity: Volume labeling adds complexity on SELinux-enabled systems

Migration Impact

  1. Existing Docker Users: Can continue using Docker without changes
  2. New Deployments: Encouraged to use Podman for security benefits
  3. Documentation: All examples show both podman and docker commands
  4. Scripts: Backup/restore scripts detect and support both engines

Technical Mitigations

  1. Abstraction: Use OCI-standard features that work identically
  2. Detection: Scripts auto-detect podman vs docker
  3. Defaults: Use patterns that work well in both engines
  4. Testing: CI/CD tests both podman and docker deployments
  5. Troubleshooting: Document common issues and solutions for both engines

Production Deployment Implications

Podman Production Deployment:

# Build image
podman build -t gondulf:latest .

# Generate systemd unit
podman generate systemd --new --files --name gondulf

# Enable and start service
sudo cp container-gondulf.service /etc/systemd/system/
sudo systemctl enable --now container-gondulf.service

Docker Production Deployment (unchanged):

# Build and start
docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d

# Enable auto-start
docker-compose restart unless-stopped

Documentation Structure

All deployment documentation will follow this pattern:

## Build Image

**Using Podman** (recommended):
```bash
podman build -t gondulf:latest .

Using Docker:

docker build -t gondulf:latest .

## Alternatives Considered

### Alternative 1: Docker Only
**Rejected**: Misses opportunity to leverage Podman's security and operational benefits. Many modern Linux distributions are standardizing on Podman.

### Alternative 2: Podman Only
**Rejected**: Too disruptive for existing Docker users. Docker remains widely deployed and understood.

### Alternative 3: Wrapper Scripts
**Rejected**: Adds complexity without significant benefit. Direct command examples are clearer.

## Implementation Guidance

### Dockerfile Compatibility

The existing Dockerfile design is already OCI-compliant and works with both engines. No changes required to Dockerfile structure.

### Compose File Compatibility

Use compose file features that work in both docker-compose and podman-compose:
- ✅ services, volumes, networks
- ✅ environment variables
- ✅ port mappings
- ✅ health checks
- ⚠️ depends_on with condition (docker-compose v3+, podman-compose limited)
- ⚠️ profiles (docker-compose, podman-compose limited)

**Mitigation**: Use compose file v3.8 features conservatively, test with both tools.

### Volume Permission Pattern

**Named Volumes** (recommended, works in both):
```yaml
volumes:
  gondulf_data:/data

Bind Mounts with SELinux Label (if needed):

volumes:
  - ./data:/data:Z  # Z = private label (recommended)
  # or
  - ./data:/data:z  # z = shared label

systemd Integration

Provide instructions for both manual systemd units and podman-generated units:

Manual systemd Unit (works for both):

[Unit]
Description=Gondulf IndieAuth Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/podman-compose -f /opt/gondulf/docker-compose.yml up
ExecStop=/usr/bin/podman-compose -f /opt/gondulf/docker-compose.yml down
Restart=always

[Install]
WantedBy=multi-user.target

Podman-Generated Unit (podman only):

podman generate systemd --new --files --name gondulf

Command Detection in Scripts

Backup/restore scripts should detect available engine:

#!/bin/bash
# Detect container engine
if command -v podman &> /dev/null; then
    CONTAINER_ENGINE="podman"
elif command -v docker &> /dev/null; then
    CONTAINER_ENGINE="docker"
else
    echo "Error: Neither podman nor docker found"
    exit 1
fi

# Use detected engine
$CONTAINER_ENGINE exec gondulf sqlite3 /data/gondulf.db ".backup /tmp/backup.db"

References

Future Considerations

  1. Kubernetes Compatibility: Podman's pod support could enable future k8s migration
  2. Multi-Container Pods: Could group nginx + gondulf in a single pod
  3. Container Security: Explore additional Podman security features (seccomp, capabilities)
  4. Image Distribution: Consider both Docker Hub and Quay.io for image hosting