Skip to content

Add staging environment #39

Description

@retlehs

Context

We need a staging environment to safely test changes before production. Staging will be gated with Caddy basic auth and use dedicated R2 buckets.


Step 1: Caddyfile — Add Conditional Basic Auth

File: deploy/ansible/roles/caddy/templates/Caddyfile.j2

Add a conditional basicauth * block. Production never defines caddy_basic_auth_enabled, so it's unaffected.

{% if caddy_basic_auth_enabled | default(false) %}
	basicauth * {
		{{ caddy_basic_auth_user }} {{ caddy_basic_auth_hash }}
	}
{% endif %}

Caddy uses bcrypt hashes natively — generate with caddy hash-password --plaintext '<password>'.

For Composer client testing, configure auth.json:

{"http-basic": {"staging.wp-packages.org": {"username": "staging", "password": "<pass>"}}}

Step 2: Ansible Multi-Environment Setup

2a. Update ansible.cfg

File: deploy/ansible/ansible.cfg

Remove inventory = inventory/hosts/production.yml. Inventory will be passed explicitly via -i flag to prevent accidental production deploys.

2b. Create staging inventory example

New file: deploy/ansible/inventory/hosts/staging.example.yml

all:
  hosts:
    wp-packages-staging:
      ansible_host: your.staging.server.ip
      ansible_user: deploy
      ansible_python_interpreter: /usr/bin/python3

2c. Create staging group_vars

New file: deploy/ansible/group_vars/staging/main.yml

Copy from production with these overrides:

app_domain: staging.wp-packages.org
app_env: staging
app_debug: "true"
go_log_level: debug
r2_cdn_public_url: "https://cdn-staging.wp-packages.org"
caddy_basic_auth_enabled: true
caddy_basic_auth_user: "staging"
caddy_basic_auth_hash: "{{ vault_caddy_basic_auth_hash }}"

Everything else (timers, users, litestream version, etc.) stays the same as production.

New file: deploy/ansible/group_vars/staging/vault.example.yml

Same as production vault.example.yml plus vault_caddy_basic_auth_hash: "REPLACE_ME".


Step 3: GitHub Actions Workflow

File: .github/workflows/deploy.yml

Add an environment input (choice: staging/production, default: staging):

  • concurrency: ${{ inputs.environment }}-deploy — independent locks per env
  • Conditionally use PROD_* or STAGING_* secrets based on the selected environment
  • Pass -i inventory/hosts/${{ inputs.environment }}.yml explicitly to ansible-playbook

GitHub repo setup (manual, one-time):
Add repo-level secrets: STAGING_SSH_PRIVATE_KEY, STAGING_ANSIBLE_VAULT_PASSWORD, STAGING_INVENTORY_YML_B64, STAGING_VAULT_YML_B64


Step 4: R2 Buckets (Manual in Cloudflare)

Create three staging buckets (using existing R2 API token):

  1. wp-packages-staging — Composer package metadata
  2. wp-packages-staging-cdn — CDN assets, with custom domain cdn-staging.wp-packages.org
  3. wp-packages-staging-backups — Litestream SQLite replicas

Step 5: DNS & Cloudflare (Manual)

  1. staging.wp-packages.org — A record to staging server IP (proxied through Cloudflare)
  2. cdn-staging.wp-packages.org — set up via R2 custom domain settings
  3. Generate a separate Cloudflare origin certificate for staging.wp-packages.org
  4. Store staging cert + key in staging vault (vault_ssl_certificate, vault_ssl_private_key)

Step 6: Provision & Verify

  1. Spin up staging VPS
  2. Add all STAGING_* secrets to the GitHub repo
  3. Run workflow: environment: staging, action: provision, ref: main
  4. Verify:
    • curl https://staging.wp-packages.org → 401 (basic auth required)
    • curl -u staging:<pass> https://staging.wp-packages.org → 200
    • R2 uploads land in staging buckets
    • composer require wp-plugin/akismet works with auth.json configured

Files to Create/Modify

File Action
deploy/ansible/roles/caddy/templates/Caddyfile.j2 Add conditional basicauth block
deploy/ansible/ansible.cfg Remove hardcoded inventory line
deploy/ansible/group_vars/staging/main.yml Create — staging config
deploy/ansible/group_vars/staging/vault.example.yml Create — vault key reference
deploy/ansible/inventory/hosts/staging.example.yml Create — inventory example
.github/workflows/deploy.yml Add environment input, conditional secrets, per-env concurrency
docs/operations.md Update for multi-environment support (inventory -i flag, staging secrets, etc.)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions