<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Caddy on StackOnward</title><link>https://stackonward.com/tags/caddy/</link><description>Recent content in Caddy on StackOnward</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 11 Sep 2026 05:18:00 +0800</lastBuildDate><atom:link href="https://stackonward.com/tags/caddy/index.xml" rel="self" type="application/rss+xml"/><item><title>Automatic HTTPS Certificate Renewal with Caddy and ACME</title><link>https://stackonward.com/posts/https-certificate-automatic-renewal/</link><pubDate>Fri, 11 Sep 2026 05:05:00 +0800</pubDate><guid>https://stackonward.com/posts/https-certificate-automatic-renewal/</guid><description>&lt;p&gt;Automatic HTTPS certificate renewal in Caddy is not a cron job. Caddy obtains a certificate, serves HTTPS, redirects HTTP, and renews in the background when the hostname, public DNS, ACME challenge path, and certificate storage all work. A running Caddy process does not prove the next ACME attempt will succeed.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/https-certificate-automatic-renewal/en/acme-renewal-flow.svg" alt="Flow from Caddy reading a hostname, completing ACME validation, storing the certificate, renewing in the background, and checking TLS from outside" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;After issuance, the certificate, private key, and ACME account state are written to the data directory. Background renewal uses the same challenge path.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="automatic-https-certificate-renewal-only-starts-when-these-conditions-hold"&gt;Automatic HTTPS certificate renewal only starts when these conditions hold&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://caddyserver.com/docs/automatic-https" target="_blank" rel="noopener"&gt;Caddy&amp;rsquo;s Automatic HTTPS documentation&lt;span class="sr-only"&gt;(opens in a new tab)&lt;/span&gt;&lt;/a&gt; requires all of the following at once:&lt;/p&gt;</description></item><item><title>Docker Compose Blue-Green: Health Check, Switch, Drain</title><link>https://stackonward.com/posts/single-server-short-downtime-deployment/</link><pubDate>Fri, 11 Sep 2026 05:00:00 +0800</pubDate><guid>https://stackonward.com/posts/single-server-short-downtime-deployment/</guid><description>&lt;p&gt;A Docker Compose blue-green deployment on one server starts the candidate, waits until the app is ready, validates and switches the Caddy upstream, proves the public hostname, drains old connections, then stops the old version. Do not delete the old container before the candidate is taking traffic.&lt;/p&gt;
&lt;p&gt;That sequence can shrink the unavailable window. The host, disk, Docker daemon, and shared database are still single points of failure, so measure the window instead of calling the result zero downtime.&lt;/p&gt;
&lt;figure&gt;
 &lt;img src="https://stackonward.com/images/posts/single-server-short-downtime-deployment/en/blue-green-release-flow.svg" alt="Old version keeps traffic on 18080; candidate starts on 18081 and passes a loopback health check; Caddy switches, the public hostname is verified, then old connections drain" loading="lazy" decoding="async"&gt;
 &lt;figcaption&gt;The gateway switch is the only public cutover. Candidate startup and warmup happen before that switch.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="docker-compose-blue-green-only-works-under-these-constraints"&gt;Docker Compose blue-green only works under these constraints&lt;/h2&gt;
&lt;p&gt;Run two app instances on one host only when the service meets all of these conditions:&lt;/p&gt;</description></item></channel></rss>