2323< input autocomplete ="off " class ="md-toggle " data-md-toggle ="search " id ="__search " type ="checkbox "/>
2424< label class ="md-overlay " for ="__drawer "> </ label >
2525< div data-md-component ="skip ">
26- < a class ="md-skip " href ="#1-adjust-the-application ">
26+ < a class ="md-skip " href ="#advanced-deployments ">
2727 Skip to content
2828 </ a >
2929</ div >
180180 </ label >
181181< ul class ="md-nav__list " data-md-component ="toc " data-md-scrollfix ="">
182182< li class ="md-nav__item ">
183+ < a class ="md-nav__link " href ="#how-it-works ">
184+ < span class ="md-ellipsis ">
185+ 🧭 How It Works
186+ </ span >
187+ </ a >
188+ </ li >
189+ < li class ="md-nav__item ">
190+ < a class ="md-nav__link " href ="#deployment-flow ">
191+ < span class ="md-ellipsis ">
192+ 🔄 Deployment Flow
193+ </ span >
194+ </ a >
195+ </ li >
196+ < li class ="md-nav__item ">
183197< a class ="md-nav__link " href ="#1-adjust-the-application ">
184198< span class ="md-ellipsis ">
185199 1. Adjust the Application
186200 </ span >
187201</ a >
188202</ li >
189203< li class ="md-nav__item ">
190- < a class ="md-nav__link " href ="#deployment ">
204+ < a class ="md-nav__link " href ="#2-start-a-new-proxy-project ">
205+ < span class ="md-ellipsis ">
206+ 2. Start a new proxy project
207+ </ span >
208+ </ a >
209+ </ li >
210+ < li class ="md-nav__item ">
211+ < a class ="md-nav__link " href ="#start-the-services ">
191212< span class ="md-ellipsis ">
192- Deployment
213+ Start the services
193214 </ span >
194215</ a >
195216</ li >
196217< li class ="md-nav__item ">
197- < a class ="md-nav__link " href ="#deploy-the-proxy ">
218+ < a class ="md-nav__link " href ="#deploying ">
198219< span class ="md-ellipsis ">
199- Deploy the Proxy
220+ Deploying
200221 </ span >
201222</ a >
202223</ li >
206227 Deploy the app
207228 </ span >
208229</ a >
209- </ li >
230+ < nav aria-label ="Deploy the app " class ="md-nav ">
231+ < ul class ="md-nav__list ">
210232< li class ="md-nav__item ">
211233< a class ="md-nav__link " href ="#flip-traffic ">
212234< span class ="md-ellipsis ">
213235 Flip traffic
214236 </ span >
215237</ a >
216238</ li >
239+ </ ul >
240+ </ nav >
241+ </ li >
217242< li class ="md-nav__item ">
218243< a class ="md-nav__link " href ="#github-actions-workflow ">
219244< span class ="md-ellipsis ">
239264 </ label >
240265< ul class ="md-nav__list " data-md-component ="toc " data-md-scrollfix ="">
241266< li class ="md-nav__item ">
267+ < a class ="md-nav__link " href ="#how-it-works ">
268+ < span class ="md-ellipsis ">
269+ 🧭 How It Works
270+ </ span >
271+ </ a >
272+ </ li >
273+ < li class ="md-nav__item ">
274+ < a class ="md-nav__link " href ="#deployment-flow ">
275+ < span class ="md-ellipsis ">
276+ 🔄 Deployment Flow
277+ </ span >
278+ </ a >
279+ </ li >
280+ < li class ="md-nav__item ">
242281< a class ="md-nav__link " href ="#1-adjust-the-application ">
243282< span class ="md-ellipsis ">
244283 1. Adjust the Application
245284 </ span >
246285</ a >
247286</ li >
248287< li class ="md-nav__item ">
249- < a class ="md-nav__link " href ="#deployment ">
288+ < a class ="md-nav__link " href ="#2-start-a-new-proxy-project ">
250289< span class ="md-ellipsis ">
251- Deployment
290+ 2. Start a new proxy project
252291 </ span >
253292</ a >
254293</ li >
255294< li class ="md-nav__item ">
256- < a class ="md-nav__link " href ="#deploy -the-proxy ">
295+ < a class ="md-nav__link " href ="#start -the-services ">
257296< span class ="md-ellipsis ">
258- Deploy the Proxy
297+ Start the services
298+ </ span >
299+ </ a >
300+ </ li >
301+ < li class ="md-nav__item ">
302+ < a class ="md-nav__link " href ="#deploying ">
303+ < span class ="md-ellipsis ">
304+ Deploying
259305 </ span >
260306</ a >
261307</ li >
265311 Deploy the app
266312 </ span >
267313</ a >
268- </ li >
314+ < nav aria-label ="Deploy the app " class ="md-nav ">
315+ < ul class ="md-nav__list ">
269316< li class ="md-nav__item ">
270317< a class ="md-nav__link " href ="#flip-traffic ">
271318< span class ="md-ellipsis ">
272319 Flip traffic
273320 </ span >
274321</ a >
275322</ li >
323+ </ ul >
324+ </ nav >
325+ </ li >
276326< li class ="md-nav__item ">
277327< a class ="md-nav__link " href ="#github-actions-workflow ">
278328< span class ="md-ellipsis ">
287337</ div >
288338< div class ="md-content " data-md-component ="content ">
289339< article class ="md-content__inner md-typeset ">
290- < h1 > Advanced Deployments</ h1 >
291- < p > The standard SuperStack has a single app, and it's upgraded in place.</ p >
340+ < h1 id ="advanced-deployments "> ⚙️ Advanced Deployments</ h1 >
341+ < p > By default, SuperStack runs as a < strong > single project</ strong > that's upgraded in place.</ p >
342+ < p > While this is simple, it has some trade-offs:</ p >
292343< ul >
293- < li > There's potentially some downtime while upgrading. </ li >
294- < li > Once an app is upgraded, you can't rollback. </ li >
295- < li > You can't test one app while another is live (blue/green) </ li >
344+ < li > Some downtime during upgrades </ li >
345+ < li > No way to test a new version while another is live (blue/green) </ li >
346+ < li > No quick rollback once upgraded </ li >
296347</ ul >
297- < p > When your app is ready for production, consider adding a traffic-switcher in
298- front of your app.</ p >
299- < p > Here's how it works:</ p >
348+ < p > When your app is ready for production, consider adding a < strong > traffic-switcher</ strong >
349+ in front it.</ p >
350+ < h2 id ="how-it-works "> 🧭 How It Works</ h2 >
351+ < p > Instead of directly exposing ports from the app, you add a small proxy project
352+ that sits in front of it.</ p >
353+ < p > The proxy’s job is to:</ p >
300354< ul >
301- < li > We stop exposing ports in the < code > app</ code > project.</ li >
302- < li > A new < code > proxy</ code > service is added with ports open.</ li >
303- < li > It's purpose is to direct traffic to the right application. (it also takes
304- over TLS termination)</ li >
305- < li > Rather than upgrading the single app, new apps containers are brought up,
306- separate to the live one, and they connect to the proxy's network.</ li >
307- < li > Test the new app before it goes live.</ li >
308- < li > Finally traffic is flipped to the new app.</ li >
355+ < li > Route incoming traffic to the active app stack</ li >
356+ < li > Make it easy to flip traffic between app versions</ li >
357+ < li > Terminate TLS</ li >
309358</ ul >
310- < p > This way, environments are < em > ephemeral, immutable and idempotent</ em > .</ p >
311359< pre class ="mermaid "> < code > flowchart TD
312- Proxy["< b > Proxy </ b > < br /> < i > Used for traffic shifting </ i > "]
313- Proxy --> App[" < b > Application </ b > < br /> < i > API Gateway, Auth, APIs, Messaging, Workers, etc. </ i > "]
314- App --> Database[" < b > Database </ b > < br /> < i > Optional </ i > "]
360+ Proxy["Traffic Router "]
361+ Proxy --> LiveApp["Live App "]
362+ NextApp["Next App "]
315363</ code > </ pre >
364+ < h2 id ="deployment-flow "> 🔄 Deployment Flow</ h2 >
365+ < ol >
366+ < li > Stop exposing ports in the app project — only the proxy will listen on :80 and :443.</ li >
367+ < li > Add a proxy project that runs Caddy (or another gateway).</ li >
368+ < li > Each time you deploy, bring up a new app stack, connected to the proxy’s
369+ network.</ li >
370+ < li > Test the new stack while the old one is still live.</ li >
371+ < li > Flip traffic in the proxy to point to the new stack.</ li >
372+ < li > Tear down the old one when ready.</ li >
373+ </ ol >
374+ < p > Ok, we need to make some changes to the repository.</ p >
316375< h2 id ="1-adjust-the-application "> 1. Adjust the Application</ h2 >
317376< p > Remove the app's exposed ports, and connect to the proxy's network:</ p >
318- < p > ```yaml title="app/compose.yaml" hl_lines="6-11,13-15"
319- services:
320- caddy:
321- build:
322- context: ./caddy
323- environment:
324- CADDY_SITE_ADDRESS: ":80"
325- networks:
326- default:
327- proxy_default:
328- aliases:
329- - ${COMPOSE_PROJECT_NAME}_caddy</ p >
330- < p > networks:
331- proxy_default:
332- external: true
333- < div class ="highlight "> < pre > < span > </ span > < code > What's changed?
334-
335- 1. The exposed `ports` were removed.
336- 2. Caddy's site address has changed to `:80` (The application layer no longer
337- handles TLS).
338- 3. We connect to the proxy's network, so the proxy can direct traffic to the
339- app.
340- 4. A container alias was added. This alias allows the proxy to target this
341- container, while still allowing Docker to manage the container name.
342-
343- Additionally, the `CADDY_SITE_ADDRESS` env var can be removed from the
344- development override file.
345-
346- ## 2. Create a new `proxy` project
347-
348- From the root of the repository, create a new `proxy` project:
349-
350- ```sh
351- mkdir proxy
352- </ code > </ pre > </ div > </ p >
353- < p > Add a compose file:</ p >
377+ < div class ="highlight "> < span class ="filename "> app/compose.yaml</ span > < pre > < span > </ span > < code > < span class ="nt "> services</ span > < span class ="p "> :</ span >
378+ < span class ="w "> </ span > < span class ="nt "> caddy</ span > < span class ="p "> :</ span >
379+ < span class ="w "> </ span > < span class ="nt "> build</ span > < span class ="p "> :</ span >
380+ < span class ="w "> </ span > < span class ="nt "> context</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ./caddy</ span >
381+ < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> environment</ span > < span class ="p "> :</ span >
382+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> CADDY_SITE_ADDRESS</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="s "> ":80"</ span >
383+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> networks</ span > < span class ="p "> :</ span >
384+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> default</ span > < span class ="p "> :</ span >
385+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> proxy_default</ span > < span class ="p "> :</ span >
386+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> aliases</ span > < span class ="p "> :</ span >
387+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="p p-Indicator "> -</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> ${COMPOSE_PROJECT_NAME}_caddy</ span >
388+ </ span >
389+ < span class ="hll "> < span class ="nt "> networks</ span > < span class ="p "> :</ span >
390+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> proxy_default</ span > < span class ="p "> :</ span >
391+ </ span > < span class ="hll "> < span class ="w "> </ span > < span class ="nt "> external</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> true</ span >
392+ </ span > </ code > </ pre > </ div >
393+ < p > What's changed?</ p >
394+ < ol >
395+ < li > The exposed ports were removed.</ li >
396+ < li > Caddy's site address has changed to < code > :80</ code > (The application layer no longer
397+ handles TLS).</ li >
398+ < li > We connect to the proxy's network, so the proxy can direct traffic to the
399+ app.</ li >
400+ < li > A container alias was added. This alias allows the proxy to target this
401+ container, while still allowing Docker to manage the container name.</ li >
402+ </ ol >
403+ < p > The < code > CADDY_SITE_ADDRESS</ code > environment variable can be removed from the override
404+ file.</ p >
405+ < h2 id ="2-start-a-new-proxy-project "> 2. Start a new < code > proxy</ code > project</ h2 >
406+ < p > From the root of the repository, create a new < code > proxy</ code > project:</ p >
407+ < div class ="highlight "> < pre > < span > </ span > < code > mkdir< span class ="w "> </ span > proxy
408+ </ code > </ pre > </ div >
409+ < p > Give it a Compose file:</ p >
354410< div class ="highlight "> < span class ="filename "> proxy/compose.yaml</ span > < pre > < span > </ span > < code > < span class ="nt "> services</ span > < span class ="p "> :</ span >
355411< span class ="w "> </ span > < span class ="nt "> caddy</ span > < span class ="p "> :</ span >
356412< span class ="w "> </ span > < span class ="nt "> build</ span > < span class ="p "> :</ span >
@@ -365,8 +421,9 @@ <h2 id="1-adjust-the-application">1. Adjust the Application</h2>
365421
366422< span class ="nt "> volumes</ span > < span class ="p "> :</ span >
367423< span class ="w "> </ span > < span class ="nt "> caddy_data</ span > < span class ="p "> :</ span >
368- < span class ="w "> </ span > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> caddy-data</ span > < span class ="w "> </ span > < span class ="c1 "> # The proxy manages TLS, so give it a persistent volume for certificates</ span >
424+ < span class ="w "> </ span > < span class ="nt "> name</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> caddy-data</ span > < span class ="w "> </ span > < span class ="c1 "> # Persistent volume for certificates</ span >
369425</ code > </ pre > </ div >
426+ < p > Add development overrides:</ p >
370427< div class ="highlight "> < span class ="filename "> proxy/compose.override.yaml</ span > < pre > < span > </ span > < code > < span class ="c1 "> # Development overrides</ span >
371428
372429< span class ="nt "> services</ span > < span class ="p "> :</ span >
@@ -376,12 +433,24 @@ <h2 id="1-adjust-the-application">1. Adjust the Application</h2>
376433< span class ="w "> </ span > < span class ="nt "> environment</ span > < span class ="p "> :</ span >
377434< span class ="w "> </ span > < span class ="nt "> CADDY_SITE_ADDRESS</ span > < span class ="p "> :</ span > < span class ="w "> </ span > < span class ="l l-Scalar l-Scalar-Plain "> :80</ span >
378435</ code > </ pre > </ div >
436+ < p > Configure Caddy:</ p >
437+ < div class ="highlight "> < pre > < span > </ span > < code > mkdir< span class ="w "> </ span > proxy/caddy
438+ </ code > </ pre > </ div >
379439< div class ="highlight "> < span class ="filename "> proxy/caddy/Caddyfile</ span > < pre > < span > </ span > < code > < span class ="p p-Indicator "> {</ span > < span class ="nv "> $CADDY_SITE_ADDRESS</ span > < span class ="p p-Indicator "> }</ span >
380440
381441< span class ="l l-Scalar l-Scalar-Plain "> reverse_proxy app_caddy:80</ span >
382442</ code > </ pre > </ div >
383- < h2 id ="deployment "> Deployment</ h2 >
384- < p > The directory structure looks like:</ p >
443+ < p > Add a Dockerfile:</ p >
444+ < div class ="highlight "> < span class ="filename "> proxy/caddy/Dockerfile</ span > < pre > < span > </ span > < code > < span class ="k "> FROM</ span > < span class ="w "> </ span > < span class ="s "> caddy:2</ span >
445+
446+ < span class ="k "> COPY</ span > < span class ="w "> </ span > Caddyfile< span class ="w "> </ span > /etc/caddy/Caddyfile
447+ </ code > </ pre > </ div >
448+ < h2 id ="start-the-services "> Start the services</ h2 >
449+ < p > Start the proxy first, then the app which connects to its network.</ p >
450+ < div class ="highlight "> < pre > < span > </ span > < code > < span class ="l l-Scalar l-Scalar-Plain "> cd proxy && docker compose up -d</ span >
451+ < span class ="l l-Scalar l-Scalar-Plain "> cd ../app && docker compose up -d</ span >
452+ </ code > </ pre > </ div >
453+ < h2 id ="deploying "> Deploying</ h2 >
385454< div class ="highlight "> < pre > < span > </ span > < code > proxy/
386455 compose.yaml
387456app/
@@ -392,12 +461,11 @@ <h2 id="deployment">Deployment</h2>
392461 compose.yaml
393462 .env
394463</ code > </ pre > </ div >
395- < h2 id ="deploy-the-proxy "> Deploy the Proxy</ h2 >
396- < p > The proxy is only deployed once.</ p >
464+ < p > The proxy is deployed once.</ p >
397465< p > On the server, create a proxy directory:</ p >
398466< div class ="highlight "> < pre > < span > </ span > < code > mkdir< span class ="w "> </ span > proxy
399467</ code > </ pre > </ div >
400- < p > Back on local, copy your Compose file to the server:</ p >
468+ < p > Copy your Compose file to the server:</ p >
401469< div class ="highlight "> < pre > < span > </ span > < code > scp< span class ="w "> </ span > proxy/compose.yaml< span class ="w "> </ span > app-backend:proxy/
402470</ code > </ pre > </ div >
403471< blockquote >
@@ -406,7 +474,7 @@ <h2 id="deploy-the-proxy">Deploy the Proxy</h2>
406474< h2 id ="deploy-the-app "> Deploy the app</ h2 >
407475< div class ="highlight "> < pre > < span > </ span > < code > docker< span class ="w "> </ span > compose< span class ="w "> </ span > up< span class ="w "> </ span > -d
408476</ code > </ pre > </ div >
409- < h2 id ="flip-traffic "> Flip traffic</ h2 >
477+ < h3 id ="flip-traffic "> Flip traffic</ h3 >
410478< div class ="highlight "> < pre > < span > </ span > < code > < span class ="nb "> cd</ span > < span class ="w "> </ span > proxy
411479docker< span class ="w "> </ span > compose< span class ="w "> </ span > < span class ="nb "> exec</ span > < span class ="w "> </ span > caddy< span class ="w "> </ span > curl< span class ="w "> </ span > -X< span class ="w "> </ span > PATCH< span class ="w "> </ span > -d< span class ="w "> </ span > < span class ="s1 "> '"newapp_caddy:80"'</ span > < span class ="w "> </ span > < span class ="se "> \</ span >
412480< span class ="w "> </ span > http://localhost:2019/config/apps/http/servers/srv0/routes/0/handle/0/upstreams/0/dial
0 commit comments