Skip to content

Commit b8215db

Browse files
committed
Add Blue/Green deployments section
1 parent 4d3b31c commit b8215db

File tree

1 file changed

+119
-5
lines changed

1 file changed

+119
-5
lines changed

docs/deploying.md

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ the remote server**. No other file should need to be copied there.
88

99
## ✅ 1. Prepare your Images
1010

11-
For services that are built, add `image:` URIs to your own container repository
12-
(e.g. your Docker Hub or GitHub Container Registry account), for example:
11+
For services that are built (they have a `build:` section in `compose.yaml`),
12+
add your own container repository image URIs (e.g. your Docker Hub or GitHub
13+
Container Registry account), for example:
1314

1415
```yaml title="compose.yaml"
1516
caddy:
17+
build:
18+
context: ./caddy
1619
image: ghcr.io/youruser/yourapp-caddy
1720

1821
postgres:
22+
build:
23+
context: ./postgres
1924
image: ghcr.io/youruser/yourapp-postgres
2025
```
2126
@@ -30,9 +35,7 @@ docker compose push
3035

3136
## 📦 3. Deploy the Compose File
3237

33-
Only `compose.yaml` is required on the remote server.
34-
35-
Copy it to your server:
38+
Copy `compose.yaml` to your server:
3639

3740
```sh
3841
scp compose.yaml youruser@yourserver:
@@ -69,3 +72,114 @@ docker compose pull
6972
docker compose up -d
7073
docker compose exec postgres migrate
7174
```
75+
76+
## Blue/Green Deployments
77+
78+
This is a deployment method where two complete stacks are running side-by-side.
79+
One is serving production traffic, while the other is idle. You deploy to the
80+
idle stack, test it, and when you’re ready you swap roles — the idle stack
81+
becomes production instantly, and the old production becomes idle. This
82+
provides near-zero downtime and an easy rollback path.
83+
84+
We’ll bring up two stacks, `blue` and `green`, with no ports exposed. A
85+
separate lightweight front proxy binds to `:80` and `:443` and routes traffic
86+
to whichever stack is active.
87+
88+
### 1. Caddyfile
89+
90+
Remove the exposed ports from the Caddy service. Also set `CADDY_SITE_ADDRESS`
91+
to only `:80` (leaving TLS termination to the front proxy):
92+
93+
```yaml title="compose.yaml"
94+
caddy:
95+
environment:
96+
CADDY_SITE_ADDRESS: :80
97+
```
98+
99+
To share data between the two stacks (database, uploads, etc.), give volumes
100+
explicit names:
101+
102+
```yaml title="compose.yaml"
103+
volumes:
104+
postgres_data:
105+
name: postgres-data
106+
user_data:
107+
name: user-data
108+
```
109+
110+
### 2. Start two Stacks
111+
112+
On the server, bring up two stacks, `blue` and `green`:
113+
114+
```sh
115+
docker compose -p blue up -d
116+
docker compose -p green up -d
117+
```
118+
119+
### 3. Front Proxy
120+
121+
The front proxy is a single Caddy container that binds `:80` and `:443` on the
122+
server and routes requests into either the Blue or Green stack.
123+
124+
On the server, create a simple `Caddyfile`:
125+
126+
```caddyfile title="Caddyfile"
127+
api.myapp.com {
128+
reverse_proxy blue_caddy:80
129+
}
130+
131+
# Optionally point a second hostname to the idle stack for testing
132+
next.myapp.com {
133+
reverse_proxy blue_caddy:80
134+
}
135+
```
136+
137+
The front proxy manages TLS, so give it a persistent volume for certificates:
138+
139+
```sh
140+
docker volume create caddy_data
141+
```
142+
143+
Start the proxy and attach it to both networks:
144+
145+
```sh
146+
docker run -d \
147+
--name front-proxy \
148+
-p 80:80 -p 443:443 \
149+
-v ./Caddyfile:/etc/caddy/Caddyfile \
150+
-v caddy_data:/data \
151+
--network blue_default \
152+
--network green_default \
153+
caddy:2
154+
```
155+
156+
### 4. Deploying
157+
158+
Update the idle stack:
159+
160+
```sh
161+
docker compose pull
162+
docker compose -p green up -d
163+
```
164+
165+
Edit the front proxy's config to flip traffic:
166+
167+
```caddyfile title="Caddyfile"
168+
api.myapp.com {
169+
reverse_proxy green_caddy:80
170+
}
171+
172+
next.myapp.com {
173+
reverse_proxy blue_caddy:80
174+
}
175+
```
176+
177+
Restart Caddy:
178+
179+
```sh
180+
docker exec front-proxy caddy reload
181+
```
182+
183+
Cutover is instant. Green is now live, and Blue is the idle stack.
184+
185+
And rollback is simple: flip the `Caddyfile` back and `caddy reload`.

0 commit comments

Comments
 (0)