No description
Find a file
Pieter fb90d77dbc feat: Add improved Nextcloud upgrade playbook (v2)
Complete rewrite of the upgrade playbook based on lessons learned
from the kikker upgrade. The v2 playbook is fully idempotent and
handles all edge cases properly.

Key improvements over v1:
1. **Idempotency** - Can be safely re-run after failures
2. **Smart version detection** - Reads actual running version, not just docker-compose.yml
3. **Stage skipping** - Automatically skips completed upgrade stages
4. **Better maintenance mode handling** - Properly enables/disables at right times
5. **Backup reuse** - Skips backup if already exists from previous run
6. **Dynamic upgrade path** - Only runs needed stages based on current version
7. **Clear status messages** - Shows what's happening at each step
8. **Proper error handling** - Fails gracefully with helpful messages

Files:
- playbooks/260123-upgrade-nextcloud-v2.yml (main playbook)
- playbooks/260123-upgrade-nextcloud-stage-v2.yml (stage tasks)

Testing:
- v1 playbook partially tested on kikker (manual intervention required)
- v2 playbook ready for full end-to-end testing

Usage:
  cd ansible/
  HCLOUD_TOKEN="..." ansible-playbook -i hcloud.yml \
    playbooks/260123-upgrade-nextcloud-v2.yml --limit <server> \
    --private-key "../keys/ssh/<server>"

The playbook will:
- Detect current version (v30/v31/v32)
- Skip stages already completed
- Create backup only if needed
- Upgrade through required stages
- Re-enable critical apps
- Update to 'latest' tag

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 21:25:44 +01:00
.claude/agents feat: Implement per-client SSH key isolation 2026-01-17 19:50:30 +01:00
ansible feat: Add improved Nextcloud upgrade playbook (v2) 2026-01-23 21:25:44 +01:00
clients docs: Remove Zitadel references and update documentation 2026-01-20 20:19:04 +01:00
keys chore: Post-workshop state - January 23rd, 2026 2026-01-23 20:36:31 +01:00
scripts chore: Post-workshop state - January 23rd, 2026 2026-01-23 20:36:31 +01:00
secrets fix: Correct docker_compose_v2 pull parameter syntax 2026-01-23 21:13:49 +01:00
tofu chore: Clean up Terraform/Tofu artifacts and improve .gitignore 2026-01-23 20:45:48 +01:00
.gitignore chore: Clean up Terraform/Tofu artifacts and improve .gitignore 2026-01-23 20:45:48 +01:00
.sops.yaml Complete SOPS secrets management setup (#5) 2025-12-27 14:23:36 +01:00
README.md docs: Remove Zitadel references and update documentation 2026-01-20 20:19:04 +01:00

Post-X Society Multi-Tenant Infrastructure

Infrastructure as Code for a scalable multi-tenant VPS platform running Nextcloud (file sync/share) on Hetzner Cloud.

🏗️ Architecture

  • Provisioning: OpenTofu (open source Terraform fork)
  • Configuration: Ansible with dynamic inventory
  • Secrets: SOPS + Age encryption
  • Hosting: Hetzner Cloud (EU-based, GDPR-compliant)
  • Identity: Authentik (OAuth2/OIDC SSO, MIT license)
  • Storage: Nextcloud (German company, AGPL 3.0)

📁 Repository Structure

infrastructure/
├── .claude/agents/          # AI agent definitions for specialized tasks
├── docs/                    # Architecture decisions and runbooks
├── tofu/                    # OpenTofu configurations for Hetzner
├── ansible/                 # Ansible playbooks and roles
├── secrets/                 # SOPS-encrypted secrets (git-safe)
├── docker/                  # Docker Compose configurations
└── scripts/                 # Deployment and management scripts

🚀 Quick Start

Prerequisites

The fastest way to deploy a client:

# 1. Ensure SOPS Age key is available (if not set)
export SOPS_AGE_KEY_FILE="./keys/age-key.txt"

# 2. Add client to terraform.tfvars
# clients = {
#   newclient = {
#     server_type = "cx22"
#     location    = "fsn1"
#     subdomain   = "newclient"
#     apps        = ["authentik", "nextcloud"]
#   }
# }

# 3. Deploy client (fully automated, ~10-15 minutes)
# The script automatically loads the Hetzner API token from SOPS
./scripts/deploy-client.sh newclient

Note: The Hetzner API token is now stored encrypted in secrets/shared.sops.yaml and loaded automatically by all scripts. No need to manually set HCLOUD_TOKEN.

The script will automatically:

  • Generate unique SSH key pair (if missing)
  • Create secrets file from template (if missing, opens in editor)
  • Provision VPS on Hetzner Cloud
  • Deploy Authentik (SSO/identity provider)
  • Deploy Nextcloud (file storage)
  • Configure OAuth2/OIDC integration
  • Set up SSL certificates
  • Create admin accounts

Result: Fully functional system, ready to use immediately!

Management Scripts

# Deploy a fresh client
./scripts/deploy-client.sh <client_name>

# Rebuild existing client (destroy + redeploy)
./scripts/rebuild-client.sh <client_name>

# Destroy client infrastructure
./scripts/destroy-client.sh <client_name>

See scripts/README.md for detailed documentation.

Manual Setup (Advanced)

Click to expand manual setup instructions
  1. Clone repository:

    git clone <repo-url>
    cd infrastructure
    
  2. Generate Age encryption key:

    age-keygen -o keys/age-key.txt
    # Store securely in password manager!
    
  3. Configure OpenTofu variables:

    cp tofu/terraform.tfvars.example tofu/terraform.tfvars
    # Edit with your Hetzner API token and configuration
    
  4. Create client secrets:

    cp secrets/clients/test.sops.yaml secrets/clients/<client>.sops.yaml
    sops secrets/clients/<client>.sops.yaml
    # Update client_name, domains, regenerate all passwords
    
  5. Provision infrastructure:

    cd tofu
    tofu init
    tofu apply
    
  6. Deploy applications:

    cd ../ansible
    export HCLOUD_TOKEN="your-token"
    export SOPS_AGE_KEY_FILE="../keys/age-key.txt"
    
    ansible-playbook -i hcloud.yml playbooks/setup.yml --limit <client>
    ansible-playbook -i hcloud.yml playbooks/deploy.yml --limit <client>
    

🎯 Project Principles

  1. EU/GDPR-first: European vendors and data residency
  2. Truly open source: Avoid source-available or restrictive licenses
  3. Client isolation: Full separation between tenants
  4. Infrastructure as Code: All changes via version control
  5. Security by default: Encryption, hardening, least privilege

📖 Documentation

🤝 Contributing

This project uses specialized AI agents for development:

  • Architect: High-level design decisions
  • Infrastructure: OpenTofu + Ansible implementation
  • Authentik: Identity provider and SSO configuration
  • Nextcloud: File sync/share configuration

See individual agent files in .claude/agents/ for responsibilities.

🔒 Security

  • Secrets are encrypted with SOPS + Age before committing
  • Age private keys are NEVER stored in this repository
  • See .gitignore for protected files

📝 License

TBD

🙋 Support

For issues or questions, please create a GitHub issue with the appropriate label:

  • agent:architect - Architecture/design questions
  • agent:infrastructure - IaC implementation
  • agent:authentik - Identity provider/SSO
  • agent:nextcloud - File sync/share