No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-01 13:04:29 +00:00
caddy Initial commit: infrastructure as code for confraria selfhost 2026-11-01 12:59:16 +00:00
forgejo Update forgejo.container SSH port configuration 2026-11-01 13:04:29 +00:00
.gitignore Initial commit: infrastructure as code for confraria selfhost 2026-11-01 12:59:16 +00:00
deploy.py Initial commit: infrastructure as code for confraria selfhost 2026-11-01 12:59:16 +00:00
pyproject.toml Initial commit: infrastructure as code for confraria selfhost 2026-11-01 12:59:16 +00:00
README.md docs: clarify Forgejo SSH port mapping (host 2222 -> container 22) 2026-11-01 13:02:04 +00:00
uv.lock Initial commit: infrastructure as code for confraria selfhost 2026-11-01 12:59:16 +00:00

Bazinga Infra

A pyinfra-based provisioning and deployment system for self-hosted services on a VPS.

Architecture

This stack uses:

  • pyinfra for initial VPS provisioning and service deployment
  • Podman for container runtime
  • Quadlets (systemd-integrated container definitions) for service lifecycle management
  • Systemd for orchestration and supervision of all services

Each service is defined as a .container quadlet in /etc/containers/systemd/, which systemd automatically converts to a native unit file. This means all services can be managed like any other systemd service: systemctl start, systemctl status, automatic restarts on failure, and simple enable/disable.

Services

Reverse Proxy (Caddy)

Why Caddy?

  • Automatic HTTPS with ACME (Let's Encrypt)
  • Zero-config TLS by default
  • Simple config syntax for routing and proxying
  • Built-in support for plug-and-play site config fragments
  • Lightweight and suitable for small VPS footprints

How it runs:

  • Deployed as a Podman quadlet: caddy.container
  • Systemd service: caddy.service
  • Configuration: /etc/caddy/Caddyfile (main) + /etc/caddy/conf.d/*.caddy (per-service snippets)
  • Persists TLS certificates and runtime state in Podman named volumes
  • Exposed on ports 80/443 (public)

Management:

systemctl status caddy
systemctl restart caddy

Git Hosting (Forgejo)

Why Forgejo?

  • Lightweight fork of Gitea
  • SQLite database (no separate DB service needed)
  • Git over SSH and HTTPS
  • Web UI for repository management
  • Large File Storage (LFS) support
  • Minimal resource footprint for small teams

How it runs:

  • Deployed as a Podman quadlet: forgejo.container
  • Systemd service: forgejo.service
  • Runs on container network: caddy.network
  • Configuration via environment variables (FORGEJO__*)
  • Data persists in one Podman named volume: forgejo-data
  • SSH exposed on host port 2222 (mapped from container port 22)
    • Clone with: git clone ssh://git@code.confraria.dev:2222/owner/repo.git
  • Web UI accessible through Caddy on port 443
  • Quadlet allows automatic restarts and graceful shutdown

Management:

systemctl status forgejo
systemctl logs forgejo
podman volume list | grep forgejo  # Check named volumes

Container Network (caddy.network)

Why a shared network?

  • Services need to communicate internally (e.g., Caddy ↔ Forgejo)
  • Podman networks provide DNS service discovery by container name
  • Cleaner than exposing services on the host network
  • Allows easy addition of new services without port conflicts

How it runs:

  • Defined as a Podman quadlet: caddy.network
  • Created and managed by systemd
  • Services reference it in their .container files via Network=caddy.network
  • Caddy can reach Forgejo via reverse_proxy forgejo:3000 in the Caddyfile

Provisioning Workflow

Step 1: Initial VPS Bootstrap

Run against a freshly provisioned VPS (as root or a user with passwordless sudo):

cd bazinga-infra
uv sync
uv run pyinfra inventory.py deploy.py

This:

  • Creates an admin user with SSH key and passwordless sudo
  • Hardens SSH (disables root login, password auth)
  • Installs base packages: podman, tmux, lnav, htop, rclone, restic

Step 2: Deploy Caddy

After bootstrap, update inventory.py to use the new admin user, then:

uv run pyinfra --sudo admin@host caddy/deploy.py

This:

  • Creates /etc/caddy and /srv/caddy directories
  • Places the Caddyfile and quadlets
  • Registers caddy.network and caddy.service with systemd
  • Starts Caddy

Step 3: Deploy Forgejo

uv run pyinfra --sudo admin@host forgejo/deploy.py

This:

  • Places the Forgejo quadlet
  • Creates the Caddy snippet to proxy traffic to Forgejo
  • Registers forgejo.service with systemd
  • Starts Forgejo
  • Reloads Caddy

Adding a New Service

To add a new service to the stack:

  1. Create service files:

    • servicename/deploy.py – pyinfra deployment script
    • servicename/servicename.container – Podman quadlet
    • servicename/servicename.caddy – Caddy reverse proxy snippet (if public)
  2. In deploy.py:

    • Create any host directories and permissions needed
    • Use files.put() to deploy the .container and .caddy files
    • Use systemd.daemon_reload() to register the quadlet
    • Use server.service() to start/restart the service
  3. In servicename.container:

    • Set Network=caddy.network to join the internal network
    • Define PublishPort only for public-facing ports (e.g., SSH, custom APIs)
    • Use Volume= for named volumes (persistent data) or bind mounts
    • Set environment variables for configuration
    • Use AutoUpdate=registry to auto-pull new images
  4. In servicename.caddy:

    • Define the domain and reverse proxy to the container by name
    • Example: reverse_proxy servicename:8080

Systemd Integration

All services are managed via systemd. Common commands:

# View service status
systemctl status caddy
systemctl status forgejo

# View logs
journalctl -u caddy -f          # Follow live logs
journalctl -u caddy --since "1 hour ago"

# Restart a service
systemctl restart caddy

# Enable/disable autostart
systemctl enable forgejo
systemctl disable forgejo

# Check all running services
systemctl list-units --type service --state running

Data Persistence

All persistent data is stored outside containers in one of these ways:

  • Podman named volumes: For application data (e.g., Forgejo repos and database)

    • Located in /var/lib/containers/storage/volumes/
    • Backed up as a unit
    • Example: forgejo-data
  • Host bind mounts: For configuration files

    • Located in /etc/ or /srv/
    • Tracked in the repo or backed up separately
    • Example: /etc/caddy/Caddyfile, /srv/forgejo/data

Backup strategy: Include all Podman named volumes and /srv/ in your backup routine.

Planned Services

  • Monitoring and alerting
  • Media or streaming platform
  • Document management or notes
  • File sharing or sync service
  • Home automation integrations