Skip to content

Commit 8439c16

Browse files
committed
Enhance guide on preview deployments with detailed setup steps and improved explanations
1 parent 5345ecf commit 8439c16

1 file changed

Lines changed: 8 additions & 11 deletions

File tree

_posts/2025-09-07-setting-up-preview-deployments.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,26 @@ image: /images/preview-deployments.jpg
1111

1212
Preview deployments are often touted as a major selling point of PaaS platforms like Vercel and Netlify — and for good reason. They automatically deploy every pull request to a unique, accessible URL before merging, enabling reviewers to test changes in a production-like environment without local setup.
1313

14-
But here's the thing: with modern AI tools like Claude Code and GitHub Actions, setting up your own preview deployment system is no longer the daunting task it once was. I managed to get a fully working setup in just 4 hours. This guide walks through exactly how to build a robust preview deployment system using GitHub Actions, Docker, and a reverse proxy.
14+
But here's the thing: with modern AI tools like Claude Code and GitHub Actions, setting up your own preview deployment system is no longer the daunting task it once was. My only manual step was installing the **DigitalOcean command-line tool** (`doctl`) — after that, **Claude Code did almost all of the setup**: writing the GitHub Actions workflows, the deploy and cleanup scripts, the Traefik configuration, and the Dockerfile. I managed to get a fully working setup in just 4 hours. This guide walks through exactly how to build a robust preview deployment system using GitHub Actions, Docker, and a reverse proxy.
1515

1616
## Core Architecture
1717

1818
### The Three-Layer Stack
1919

2020
**1. GitHub Actions Workflow**
21-
The workflow triggers on PR events (open, update, close) and orchestrates the entire deployment pipeline. It builds Docker images, pushes them to a registry, and triggers deployment scripts via SSH.
21+
The workflow triggers on PR events (open, update, close) and orchestrates the entire deployment pipeline. It builds Docker images, pushes them to the **DigitalOcean Container Registry**, and triggers deployment scripts via SSH. Once deployed, it **posts a comment on the PR** with the preview URLs so reviewers can click straight through.
2222

2323
**2. Containerized Application**
24-
Each PR runs in its own Docker container with defined resource limits. This ensures isolation between previews and prevents resource exhaustion from runaway processes.
24+
Each PR runs in its own Docker container with defined resource limits. In my case each container is a multi-service setup: **Supervisor manages Nginx, Gunicorn (3 workers, 2 threads), and Memcached** inside a single container. This mirrors the production stack so the preview is a faithful replica. Resource limits ensure isolation and prevent exhaustion from runaway processes.
2525

2626
**3. Reverse Proxy with SSL**
27-
A reverse proxy (Traefik, Nginx, or Caddy) handles routing requests to the correct container based on subdomain, while automatically managing SSL certificates via Let's Encrypt.
27+
A reverse proxy handles routing requests to the correct container based on subdomain, while automatically managing SSL certificates via Let's Encrypt. I use **Traefik** for this because of its excellent Docker integration. In my setup each container gets **3 Traefik routers** — one for each domain the app serves (`keepthescore.com`, `leaderboarded.com`, and `scorejudge.com`) — so reviewers can test all branded entry points.
2828

2929
## Implementation Steps
3030

3131
### Step 1: Infrastructure Setup
3232

33-
Provision a dedicated server (4GB RAM minimum recommended) with Docker installed. Create a wildcard DNS record pointing `*.preview.yourdomain.com` to your server's IP address. This enables infinite subdomains without manual DNS configuration.
33+
Provision a dedicated server with Docker installed. I use a **DigitalOcean droplet in Frankfurt** (4GB RAM, 2 vCPUs, ~$30–40/month). Create a wildcard DNS record pointing `*.preview.yourdomain.com` to your server's IP address. This enables infinite subdomains without manual DNS configuration.
3434

3535
### Step 2: Reverse Proxy Configuration
3636

@@ -60,20 +60,20 @@ Write idempotent deployment scripts that handle:
6060
- **Container lifecycle**: Stop and remove existing containers before deploying
6161
- **Environment configuration**: Pass PR-specific environment variables
6262
- **Resource limits**: Enforce memory and CPU constraints
63-
- **Health checks**: Verify the deployment is accessible before reporting success
63+
- **Health checks**: Verify the deployment is accessible before reporting success, including **SSL certificate verification**
6464
6565
### Step 5: Automated Cleanup
6666
6767
Implement two cleanup mechanisms:
6868
6969
- **Immediate**: Remove containers when PRs are closed
70-
- **Scheduled**: Clean up orphaned deployments older than X days (prevents resource leaks)
70+
- **Scheduled**: A **cron job running daily at 2 AM** removes preview containers **older than 7 days** and prunes unused Docker resources (images, volumes, networks)
7171
7272
## Critical Pitfalls and Solutions
7373
7474
### Pitfall 1: Production Database Access
7575
76-
**The Issue**: The PR in question connected preview deployments directly to the production database. While this provides realistic testing, it creates significant risks:
76+
**The Issue**: My preview deployments connect directly to the **production database with full read/write access**. While this provides the most realistic testing possible, it creates significant risks:
7777
7878
- Test data polluting production
7979
- Accidental data deletion or corruption
@@ -170,15 +170,12 @@ Implement logging and monitoring:
170170

171171
- **Multi-stage builds**: Separate build and runtime stages
172172
- **Layer caching**: Order Dockerfile commands from least to most frequently changing
173-
- **Parallel builds**: Use `docker buildx` for parallel stage execution
174173
- **Registry caching**: Push and pull cache layers from the registry
175174

176175
### Deployment Speed
177176

178177
- **Pre-pull base images**: Keep common base images on the server
179-
- **Parallel operations**: Deploy multiple containers simultaneously
180178
- **Health check optimization**: Use lightweight health endpoints
181-
- **Connection pooling**: Reuse SSH connections for multiple operations
182179

183180
## Scaling Considerations
184181

0 commit comments

Comments
 (0)