Skip to content

Conversation

@noahwhite
Copy link
Owner

@noahwhite noahwhite commented Feb 3, 2026

Summary

This PR makes the Ghost Docker Compose stack configuration ephemeral by deploying it via Ignition at boot time, while keeping secrets and persistent data on block storage.

Architecture

Component Location Source
compose.yml /etc/ghost-compose/ Ignition
.env.config /etc/ghost-compose/ Ignition
.env.secrets /var/mnt/storage/ghost-compose/ Block storage (manual)
caddy/* /etc/ghost-compose/caddy/ Ignition
mysql-init/* /etc/ghost-compose/mysql-init/ Ignition

Key Changes

  • Templates: Added compose.yml.tftpl, env.config.tftpl, Caddyfile, and snippets under opentofu/modules/vultr/instance/userdata/ghost-compose/
  • Ignition: Updated ghost.bu to deploy config files to /etc/ghost-compose/
  • Service: Changed ghost-compose.service WorkingDirectory to /etc/ghost-compose
  • Variables: Added ghost_domain, ghost_admin_domain, admin_ip, mail_smtp_user to instance module
  • Workflows: Pass TF_VAR_admin_ip for Caddy ACL configuration
  • Documentation: Updated CLAUDE.md and added migration runbook

Security

  • No secrets in Ignition/OpenTofu state - only .env.config with non-sensitive values
  • Secrets isolated to .env.secrets on block storage with 0600 permissions
  • Caddyfile uses {$HEALTH_CHECK_TOKEN} environment variable instead of hardcoded value

Migration

Before deploying, follow the migration runbook at docs/runbooks/env-secrets-migration.md to:

  1. Create .env.secrets on block storage with the required secrets
  2. Clean up the Tailscale device from admin console

Note: This will recreate the instance (Ignition config is immutable).

Test Plan

  • .env.secrets created on block storage before deployment
  • Tailscale device cleaned up before deployment
  • PR plan shows instance recreation
  • Post-deployment: Ghost site loads at https://separationofconcerns.dev
  • Post-deployment: Health check works with token
  • Post-deployment: No secrets in /etc/ghost-compose/ files

Linear Issues

Closes GHO-46, GHO-47, GHO-48, GHO-49, GHO-50, GHO-51, GHO-52, GHO-53, GHO-54

Deploy Ghost Compose configuration via Ignition at boot time while
keeping secrets and persistent data on block storage.

Architecture changes:
- Config files deployed to /etc/ghost-compose/ (ephemeral via Ignition)
- Secrets remain at /var/mnt/storage/ghost-compose/.env.secrets
- Caddyfile uses {$VAR} environment variable substitution
- compose.yml sources both .env.config and .env.secrets

Files added:
- opentofu/modules/vultr/instance/userdata/ghost-compose/ templates
- docs/runbooks/env-secrets-migration.md

Files modified:
- ghost.bu: adds storage.files for ghost-compose config
- ghost-compose.service: WorkingDirectory now /etc/ghost-compose
- instance module: templatefile processing and new variables
- workflows: pass TF_VAR_admin_ip for Caddy ACL
- CLAUDE.md: document new architecture and upstream sync workflow

Closes: GHO-46, GHO-47, GHO-48, GHO-49, GHO-50, GHO-51, GHO-52, GHO-53, GHO-54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants