@@ -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"
1516caddy :
17+ build :
18+ context : ./caddy
1619 image : ghcr.io/youruser/yourapp-caddy
1720
1821postgres :
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
3841scp compose.yaml youruser@yourserver:
@@ -69,3 +72,114 @@ docker compose pull
6972docker compose up -d
7073docker 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