You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
13
13
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.
15
15
16
16
## Core Architecture
17
17
18
18
### The Three-Layer Stack
19
19
20
20
**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.
22
22
23
23
**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.
25
25
26
26
**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.
28
28
29
29
## Implementation Steps
30
30
31
31
### Step 1: Infrastructure Setup
32
32
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.
34
34
35
35
### Step 2: Reverse Proxy Configuration
36
36
@@ -60,20 +60,20 @@ Write idempotent deployment scripts that handle:
60
60
- **Container lifecycle**: Stop and remove existing containers before deploying
- **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**
64
64
65
65
### Step 5: Automated Cleanup
66
66
67
67
Implement two cleanup mechanisms:
68
68
69
69
- **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)
71
71
72
72
## Critical Pitfalls and Solutions
73
73
74
74
### Pitfall 1: Production Database Access
75
75
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:
77
77
78
78
- Test data polluting production
79
79
- Accidental data deletion or corruption
@@ -170,15 +170,12 @@ Implement logging and monitoring:
170
170
171
171
- **Multi-stage builds**: Separate build and runtime stages
172
172
- **Layer caching**: Order Dockerfile commands from least to most frequently changing
173
-
- **Parallel builds**: Use `docker buildx` for parallel stage execution
174
173
- **Registry caching**: Push and pull cache layers from the registry
175
174
176
175
### Deployment Speed
177
176
178
177
- **Pre-pull base images**: Keep common base images on the server
0 commit comments