Preview Environments
Preview environments allow developers to see changes before merging into main. Each pull request gets a temporary deployment with its own URL.
How It Works
Pull Request opened/updated
|
v
GitHub Actions (preview.yml) triggered
|
v
Deploy preview container + DB schema
|
v
pr-42.preview.anulectra.com
Each preview environment gets:
- Separate application container (project name
{APP_NAME}-pr-{N}) - Isolated database schema (
pr_{N}in the app's existing database) - Independent Caddyfile route
- Automatic PR comment with preview URL
DNS Configuration
A wildcard DNS A record points all preview subdomains to the platform server:
This allows any preview subdomain to resolve automatically:
GitHub Secrets
Apps need one additional secret beyond the standard 4:
| Secret | Example | Purpose |
|---|---|---|
PREVIEW_DOMAIN |
anulectra.com |
Base domain for preview URLs |
The preview URL is constructed as pr-{N}.preview.{PREVIEW_DOMAIN}.
Deployment Workflow
The preview workflow (.github/workflows/preview.yml) triggers on pull request events:
Deploy (opened/synchronize/reopened)
- SSH to server, clone repo to
/opt/apps/{APP_NAME}-pr-{N}(or pull if it already exists) - Checkout the PR branch in the dedicated preview directory
- Create PostgreSQL schema
pr_{N}in the app's database - Copy production
.envfrom/opt/apps/{APP_NAME}/deploy/.envand generate.env.pr-{N}with schema-awareDATABASE_URL - Build and start containers:
docker compose -p {APP_NAME}-pr-{N} -f deploy/docker-compose.yml up -d --build - Run Alembic migrations against the preview schema
- Write Caddyfile to
/opt/platform/caddy-apps/{APP_NAME}-pr-{N}.caddy - Reload Caddy
- Post/update PR comment with preview URL
Cleanup (closed)
- Stop and remove containers:
docker compose -p {APP_NAME}-pr-{N} down --rmi local - Drop schema:
DROP SCHEMA IF EXISTS pr_{N} CASCADE - Remove Caddyfile:
rm /opt/platform/caddy-apps/{APP_NAME}-pr-{N}.caddy - Reload Caddy
- Remove the preview directory:
rm -rf /opt/apps/{APP_NAME}-pr-{N}
Database Schema Isolation
Preview environments use PostgreSQL schemas rather than separate databases:
- Production: uses
{app}_dbdatabase, defaultpublicschema - Preview PR 42: uses
{app}_dbdatabase, schemapr_42
The schema-aware DATABASE_URL uses the options parameter:
Alembic migrations run inside the preview container against the PR schema automatically.
Container Naming
Preview containers use the Docker Compose project name {APP_NAME}-pr-{N}:
Caddy Routing
Each preview gets a Caddyfile at /opt/platform/caddy-apps/{APP_NAME}-pr-{N}.caddy:
This uses the existing import /opt/platform/caddy-apps/*.caddy pattern — no platform Caddy config changes are needed.
Concurrent Previews
Multiple PRs can be previewed simultaneously. Each gets its own: - Container set (unique project name) - Database schema (unique schema name) - Caddy route (unique Caddyfile)
There is no limit beyond server resources.