X509 Certificate Error on Initial Caddy Server Startup

1. The problem I’m having:

Setup:
I am running a Caddy Server on an EC2 instance to function as a reverse proxy. The mappings for the reverse proxy (which map subdomains to backend services) are set by another service using the Caddy REST API. The Caddy Server also functions as an ACME server for local HTTPS between itself (the reverse proxy) and the backend services.

Issue:
When I initially start my Caddy Server and send a request to the server (to be forwarded), I get the error: x509: certificate signed by unknown authority. However, just rebooting the server (with caddy stop and then caddy start --config caddy.json) resolves the issue and I am able to send a request without error.

This is a consistent problem when starting my Caddy Server on a new EC2 instance. The temporary workaround for me is just to adjust my EC2 start script to include:

./caddy start --config caddy.json
./caddy stop
./caddy start --config caddy.json

However, I would like to figure out the root cause of this issue.

2. Error messages and/or full log output:

Logs (truncated due to character limits) after running ./caddy start --config caddy.json and making a request to the server (that fails).

2023/07/14 18:55:48.342	info	tls.cache.maintenance	started background certificate maintenance	{"cache": "0x400045f490"}
2023/07/14 18:55:48.345	info	http	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2023/07/14 18:55:48.345	info	http	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2023/07/14 18:55:48.382	info	http	enabling HTTP/3 listener	{"addr": ":443"}
2023/07/14 18:55:48.383	debug	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": true}
2023/07/14 18:55:48.383	info	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:55:48.383	debug	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2023/07/14 18:55:48.383	info	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:55:48.383	info	http	enabling automatic TLS certificate management	{"domains": ["10.1.0.10", "*.inductor-test-jkunzler.link"]}
2023/07/14 18:55:48.384	info	tls.obtain	acquiring lock	{"identifier": "10.1.0.10"}
2023/07/14 18:55:48.385	info	tls.obtain	acquiring lock	{"identifier": "*.inductor-test-jkunzler.link"}
2023/07/14 18:55:48.386	info	tls.obtain	lock acquired	{"identifier": "10.1.0.10"}
2023/07/14 18:55:48.386	info	tls.obtain	obtaining certificate	{"identifier": "10.1.0.10"}
2023/07/14 18:55:48.386	debug	events	event	{"name": "cert_obtaining", "id": "5ef3f0e7-7d38-4209-a038-0728b1d079e3", "origin": "tls", "data": {"identifier":"10.1.0.10"}}
2023/07/14 18:55:48.386	debug	tls.obtain	trying issuer 1/1	{"issuer": "local"}
2023/07/14 18:55:48.387	debug	pki.ca.local	using intermediate signer	{"serial": "133945864164094359504464780601471048988", "not_before": "2023-07-14 18:55:48 +0000 UTC", "not_after": "2023-07-21 18:55:48 +0000 UTC"}
2023/07/14 18:55:48.388	info	tls.obtain	certificate obtained successfully	{"identifier": "10.1.0.10"}
2023/07/14 18:55:48.389	debug	events	event	{"name": "cert_obtained", "id": "3b7a3f4d-21c5-461e-bc72-f2a71c0a5fa9", "origin": "tls", "data": {"identifier":"10.1.0.10","issuers":"local","renewal":false,"storage_key":"10.1.0.10"}}
2023/07/14 18:55:48.389	info	tls.obtain	releasing lock	{"identifier": "10.1.0.10"}
2023/07/14 18:55:48.390	warn	tls	stapling OCSP	{"error": "no OCSP stapling for [10.1.0.10]: no OCSP server specified in certificate", "identifiers": ["10.1.0.10"]}
2023/07/14 18:55:48.390	debug	tls.cache	added certificate to cache	{"subjects": ["10.1.0.10"], "expiration": "2023/07/15 06:55:49.000", "managed": true, "issuer_key": "local", "hash": "e12ed862c8b9ccb0dd988efedf741e1a5fd117f46c73b7d0b7a02dfb4af9dfd2", "cache_size": 1, "cache_capacity": 10000}
2023/07/14 18:55:48.390	debug	events	event	{"name": "cached_managed_cert", "id": "be94d3e6-c791-4e6c-88ee-c4613fdaedd3", "origin": "tls", "data": {"sans":["10.1.0.10"]}}
2023/07/14 18:55:48.391	info	tls.obtain	lock acquired	{"identifier": "*.inductor-test-jkunzler.link"}
2023/07/14 18:55:48.391	info	tls.obtain	obtaining certificate	{"identifier": "*.inductor-test-jkunzler.link"}
2023/07/14 18:55:48.391	debug	events	event	{"name": "cert_obtaining", "id": "60f1d7df-f0ee-424b-8a83-9e0417edb1c9", "origin": "tls", "data": {"identifier":"*.inductor-test-jkunzler.link"}}
2023/07/14 18:55:48.391	debug	tls.obtain	trying issuer 1/2	{"issuer": "acme-staging-v02.api.letsencrypt.org-directory"}
2023/07/14 18:55:48.411	warn	pki.ca.local	installing root certificate (you might be prompted for password)	{"path": "storage:pki/authorities/local/root.crt"}
2023/07/14 18:55:48.565	debug	http.acme_client	http request	{"method": "GET", "url": "https://acme-staging-v02.api.letsencrypt.org/directory", "headers": {"User-Agent":["Caddy/2.6.4 CertMagic acmez (linux; arm64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["826"],"Content-Type":["application/json"],"Date":["Fri, 14 Jul 2023 18:55:48 GMT"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2023/07/14 18:55:48.620	debug	http.acme_client	http request	{"method": "HEAD", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce", "headers": {"User-Agent":["Caddy/2.6.4 CertMagic acmez (linux; arm64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Date":["Fri, 14 Jul 2023 18:55:48 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["BEB90u28Lt8NxeflW0H023D2bb6v5m5MarpjeIu9RTOXIGU"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
2023/07/14 18:55:48.689	debug	http.acme_client	http request	{"method": "POST", "url": "https://acme-staging-v02.api.letsencrypt.org/acme/new-acct", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.6.4 CertMagic acmez (linux; arm64)"]}, "response_headers": {"Boulder-Requester":["110940734"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["267"],"Content-Type":["application/json"],"Date":["Fri, 14 Jul 2023 18:55:48 GMT"],"Link":["<https://acme-staging-v02.api.letsencrypt.org/directory>;rel=\"index\"","<https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf>;rel=\"terms-of-service\""],"Location":["https://acme-staging-v02.api.letsencrypt.org/acme/acct/110940734"],"Replay-Nonce":["BEB9JYe21Q4NohErKSuU4kMS-1ICmv1eAfidRnZ8AiNCEzw"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 201}
2023/07/14 18:55:48.690	info	http	waiting on internal rate limiter	{"identifiers": ["*.inductor-test-jkunzler.link"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}
2023/07/14 18:55:48.690	info	http	done waiting on internal rate limiter	{"identifiers": ["*.inductor-test-jkunzler.link"], "ca": "https://acme-staging-v02.api.letsencrypt.org/directory", "account": ""}

2023/07/14 18:57:44.457	debug	http.log.error	x509: certificate signed by unknown authority	{"request": {"remote_ip": "174.21.143.134", "remote_port": "60278", "proto": "HTTP/2.0", "method": "GET", "host": "app-1-8.inductor-test-jkunzler.link", "uri": "/favicon.ico", "headers": {"Sec-Ch-Ua-Platform": ["\"macOS\""], "Sec-Fetch-Site": ["same-origin"], "Sec-Fetch-Mode": ["no-cors"], "Accept-Encoding": ["gzip, deflate, br"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Brave\";v=\"114\""], "Sec-Ch-Ua-Mobile": ["?0"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"], "Sec-Fetch-Dest": ["image"], "Referer": ["https://app-1-8.inductor-test-jkunzler.link/"], "Accept": ["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"], "Sec-Gpc": ["1"], "Accept-Language": ["en-US,en;q=0.6"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "app-1-8.inductor-test-jkunzler.link"}}, "duration": 0.016727965, "status": 502, "err_id": "tajubzwc4", "err_trace": "reverseproxy.statusError (reverseproxy.go:1299)"}

A continuation of the logs after running caddy stop, a second ./caddy start --config caddy.json, and a second request to the server (that is successful):

2023/07/14 18:58:37.338	debug	events	event	{"name": "tls_get_certificate", "id": "627eb7fa-8e9b-4e08-a070-0176f011a42b", "origin": "tls", "data": {"client_hello":{"CipherSuites":[49195,49199,49196,49200,52393,52392,49161,49171,49162,49172,156,157,47,53,49170,10,4865,4866,4867],"ServerName":"","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2052,1027,2055,2053,2054,1025,1281,1537,1283,1539,513,515],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"Conn":{}}}}
2023/07/14 18:58:37.338	debug	tls.handshake	choosing certificate	{"identifier": "10.1.0.10", "num_choices": 1}
2023/07/14 18:58:37.338	debug	tls.handshake	default certificate selection results	{"identifier": "10.1.0.10", "subjects": ["10.1.0.10"], "managed": true, "issuer_key": "local", "hash": "e12ed862c8b9ccb0dd988efedf741e1a5fd117f46c73b7d0b7a02dfb4af9dfd2"}
2023/07/14 18:58:37.338	debug	tls.handshake	matched certificate in cache	{"remote_ip": "10.1.0.51", "remote_port": "57794", "subjects": ["10.1.0.10"], "managed": true, "expiration": "2023/07/15 06:55:49.000", "hash": "e12ed862c8b9ccb0dd988efedf741e1a5fd117f46c73b7d0b7a02dfb4af9dfd2"}
2023/07/14 18:58:37.644	debug	pki.ca.local	using intermediate signer	{"serial": "133945864164094359504464780601471048988", "not_before": "2023-07-14 18:55:48 +0000 UTC", "not_after": "2023-07-21 18:55:48 +0000 UTC"}
2023/07/14 18:59:16.548	info	tls.cache.maintenance	started background certificate maintenance	{"cache": "0x40001fb490"}
2023/07/14 18:59:16.548	info	http	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2023/07/14 18:59:16.548	info	http	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2023/07/14 18:59:16.549	debug	http.handlers.acme_server	loaded preexisting CA database	{"db_key": "local"}
2023/07/14 18:59:16.567	info	http	enabling HTTP/3 listener	{"addr": ":443"}
2023/07/14 18:59:16.567	debug	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": true}
2023/07/14 18:59:16.567	info	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:59:16.567	debug	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2023/07/14 18:59:16.567	info	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:59:16.567	info	http	enabling automatic TLS certificate management	{"domains": ["10.1.0.10", "*.inductor-test-jkunzler.link"]}
2023/07/14 18:59:16.568	warn	tls	stapling OCSP	{"error": "no OCSP stapling for [10.1.0.10]: no OCSP server specified in certificate", "identifiers": ["10.1.0.10"]}
2023/07/14 18:59:16.568	debug	tls.cache	added certificate to cache	{"subjects": ["10.1.0.10"], "expiration": "2023/07/15 06:55:49.000", "managed": true, "issuer_key": "local", "hash": "e12ed862c8b9ccb0dd988efedf741e1a5fd117f46c73b7d0b7a02dfb4af9dfd2", "cache_size": 1, "cache_capacity": 10000}
2023/07/14 18:59:16.568	debug	events	event	{"name": "cached_managed_cert", "id": "10ec1a96-0a83-4420-bb69-8da6b0a8235a", "origin": "tls", "data": {"sans":["10.1.0.10"]}}
2023/07/14 18:59:16.568	debug	tls	loading managed certificate	{"domain": "*.inductor-test-jkunzler.link", "expiration": "2023/10/12 17:56:18.000", "issuer_key": "acme-staging-v02.api.letsencrypt.org-directory", "storage": "FileStorage:/var/lib/caddy"}
2023/07/14 18:59:16.568	debug	tls.cache	added certificate to cache	{"subjects": ["*.inductor-test-jkunzler.link"], "expiration": "2023/10/12 17:56:18.000", "managed": true, "issuer_key": "acme-staging-v02.api.letsencrypt.org-directory", "hash": "d3c557fdda8c785a264983438420ea3774084e3ee7b5095b37a5f092b5f929a4", "cache_size": 2, "cache_capacity": 10000}
2023/07/14 18:59:16.568	debug	events	event	{"name": "cached_managed_cert", "id": "01eb083b-329f-4ac5-ae3e-61f7e8555249", "origin": "tls", "data": {"sans":["*.inductor-test-jkunzler.link"]}}
2023/07/14 18:59:16.569	warn	pki.ca.local	installing root certificate (you might be prompted for password)	{"path": "storage:pki/authorities/local/root.crt"}
2023/07/14 18:59:17.868	debug	http	servers shutting down with eternal grace period
2023/07/14 18:59:17.869	info	tls.cache.maintenance	stopped background certificate maintenance	{"cache": "0x400057b880"}
2023/07/14 18:59:34.613	debug	http	servers shutting down with eternal grace period
2023/07/14 18:59:34.613	info	tls.cache.maintenance	stopped background certificate maintenance	{"cache": "0x40001fb490"}
2023/07/14 18:59:34.613	debug	http.handlers.acme_server	unloading unused CA database	{"db_key": "local"}
2023/07/14 18:59:48.483	info	tls.cache.maintenance	started background certificate maintenance	{"cache": "0x400056ce00"}
2023/07/14 18:59:48.483	info	http	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2023/07/14 18:59:48.483	info	http	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2023/07/14 18:59:48.542	info	pki.ca.local	root certificate is already trusted by system	{"path": "storage:pki/authorities/local/root.crt"}
2023/07/14 18:59:48.543	debug	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2023/07/14 18:59:48.543	info	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:59:48.543	info	http	enabling HTTP/3 listener	{"addr": ":443"}
2023/07/14 18:59:48.543	info	tls	cleaning storage unit	{"description": "FileStorage:/var/lib/caddy"}
2023/07/14 18:59:48.544	debug	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": true}
2023/07/14 18:59:48.544	info	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2023/07/14 18:59:48.544	info	http	enabling automatic TLS certificate management	{"domains": ["10.1.0.10", "*.inductor-test-jkunzler.link"]}
2023/07/14 18:59:48.545	info	tls	finished cleaning storage units
2023/07/14 18:59:48.545	warn	tls	stapling OCSP	{"error": "no OCSP stapling for [10.1.0.10]: no OCSP server specified in certificate", "identifiers": ["10.1.0.10"]}
2023/07/14 18:59:48.545	debug	tls.cache	added certificate to cache	{"subjects": ["10.1.0.10"], "expiration": "2023/07/15 06:55:49.000", "managed": true, "issuer_key": "local", "hash": "e12ed862c8b9ccb0dd988efedf741e1a5fd117f46c73b7d0b7a02dfb4af9dfd2", "cache_size": 1, "cache_capacity": 10000}
2023/07/14 18:59:48.545	debug	events	event	{"name": "cached_managed_cert", "id": "e98830e8-10b7-4bff-9d89-454e2ee8a8f6", "origin": "tls", "data": {"sans":["10.1.0.10"]}}
2023/07/14 18:59:48.546	debug	tls	loading managed certificate	{"domain": "*.inductor-test-jkunzler.link", "expiration": "2023/10/12 17:56:18.000", "issuer_key": "acme-staging-v02.api.letsencrypt.org-directory", "storage": "FileStorage:/var/lib/caddy"}

2023/07/14 19:00:34.242	debug	http.handlers.reverse_proxy	selected upstream	{"dial": "10.1.0.146:443", "total_upstreams": 1}
2023/07/14 19:00:34.263	debug	http.handlers.reverse_proxy	upstream roundtrip	{"upstream": "{http.vars.backend_hostport}", "duration": 0.020763756, "request": {"remote_ip": "174.21.143.134", "remote_port": "60296", "proto": "HTTP/2.0", "method": "GET", "host": "10.1.0.146:443", "uri": "/inductor_internal/frontend/static/js/main.742afa3c.js", "headers": {"X-Forwarded-For": ["174.21.143.134"], "Sec-Gpc": ["1"], "Accept-Encoding": ["gzip, deflate, br"], "Accept": ["*/*"], "Referer": ["https://app-1-10.inductor-test-jkunzler.link/"], "Accept-Language": ["en-US,en;q=0.6"], "Sec-Ch-Ua-Platform": ["\"macOS\""], "Sec-Fetch-Dest": ["script"], "X-Forwarded-Host": ["app-1-10.inductor-test-jkunzler.link"], "Sec-Fetch-Site": ["same-origin"], "Sec-Fetch-Mode": ["no-cors"], "Sec-Ch-Ua-Mobile": ["?0"], "X-Forwarded-Proto": ["https"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Brave\";v=\"114\""], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "app-1-10.inductor-test-jkunzler.link"}}, "headers": {"Content-Type": ["application/javascript"], "Date": ["Fri, 14 Jul 2023 19:00:34 GMT"], "Etag": ["061f62e762093e361813d38adcf4b997"], "Last-Modified": ["Fri, 28 Apr 2023 21:39:01 GMT"], "Server": ["Caddy", "uvicorn"], "Content-Length": ["1360865"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}, "status": 200}
2023/07/14 19:00:34.834	debug	http.handlers.reverse_proxy	selected upstream	{"dial": "10.1.0.146:443", "total_upstreams": 1}
2023/07/14 19:00:34.845	debug	http.handlers.reverse_proxy	upstream roundtrip	{"upstream": "{http.vars.backend_hostport}", "duration": 0.011135525, "request": {"remote_ip": "174.21.143.134", "remote_port": "60296", "proto": "HTTP/2.0", "method": "POST", "host": "10.1.0.146:443", "uri": "/inductor_internal/ui/", "headers": {"X-Forwarded-For": ["174.21.143.134"], "Origin": ["https://app-1-10.inductor-test-jkunzler.link"], "Accept-Language": ["en-US,en;q=0.6"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"], "Sec-Fetch-Dest": ["empty"], "Sec-Ch-Ua-Platform": ["\"macOS\""], "Sec-Fetch-Mode": ["cors"], "X-Forwarded-Proto": ["https"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Brave\";v=\"114\""], "X-Forwarded-Host": ["app-1-10.inductor-test-jkunzler.link"], "Referer": ["https://app-1-10.inductor-test-jkunzler.link/"], "Sec-Gpc": ["1"], "Content-Length": ["2"], "Sec-Ch-Ua-Mobile": ["?0"], "Content-Type": ["application/json"], "Accept": ["*/*"], "Sec-Fetch-Site": ["same-origin"], "Accept-Encoding": ["gzip, deflate, br"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "app-1-10.inductor-test-jkunzler.link"}}, "headers": {"Content-Type": ["application/json"], "Date": ["Fri, 14 Jul 2023 19:00:34 GMT"], "Server": ["Caddy", "uvicorn"], "Content-Length": ["454"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}, "status": 200}
2023/07/14 19:00:34.907	debug	http.handlers.reverse_proxy	selected upstream	{"dial": "10.1.0.146:443", "total_upstreams": 1}
2023/07/14 19:00:34.910	debug	http.handlers.reverse_proxy	upstream roundtrip	{"upstream": "{http.vars.backend_hostport}", "duration": 0.002987035, "request": {"remote_ip": "174.21.143.134", "remote_port": "60296", "proto": "HTTP/2.0", "method": "GET", "host": "10.1.0.146:443", "uri": "/inductor_internal/frontend/favicon.png", "headers": {"Sec-Ch-Ua-Mobile": ["?0"], "Sec-Ch-Ua": ["\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Brave\";v=\"114\""], "Sec-Fetch-Mode": ["no-cors"], "X-Forwarded-Proto": ["https"], "Sec-Fetch-Site": ["same-origin"], "Sec-Fetch-Dest": ["image"], "X-Forwarded-For": ["174.21.143.134"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"], "Sec-Ch-Ua-Platform": ["\"macOS\""], "Accept-Language": ["en-US,en;q=0.6"], "Sec-Gpc": ["1"], "Accept-Encoding": ["gzip, deflate, br"], "Referer": ["https://app-1-10.inductor-test-jkunzler.link/"], "Accept": ["image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"], "X-Forwarded-Host": ["app-1-10.inductor-test-jkunzler.link"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "app-1-10.inductor-test-jkunzler.link"}}, "headers": {"Content-Type": ["image/png"], "Date": ["Fri, 14 Jul 2023 19:00:34 GMT"], "Etag": ["45a19c829ca52d3a6677c7277829e1ce"], "Last-Modified": ["Fri, 28 Apr 2023 21:38:18 GMT"], "Server": ["Caddy", "uvicorn"], "Content-Length": ["153"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}, "status": 200}

3. Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

4. How I installed and ran Caddy:

a. System environment:

Amazon Linux 2023

b. Command:

#!/bin/bash
mkdir /home/reverse_proxy
cd /home/reverse_proxy
dnf -y update
aws s3 cp s3://inductor-cloud/reverse_proxy/                 . --recursive --include "*"
mkdir -p /var/lib/caddy/pki/authorities/local/
cp root.crt root.key /var/lib/caddy/pki/authorities/local/
echo "export PRIVATE_IP=$(ifconfig ens5 | grep 'inet ' |                 awk '{print $2}')" >> /etc/environment
echo "export XDG_CONFIG_HOME=/config" >> /etc/environment
echo "export XDG_DATA_HOME=/data" >> /etc/environment
source /etc/environment
tar -xzvf caddy.tar.gz
./caddy start --config caddy.json

d. My complete Caddy config:

Reverse Proxy:

{
    "admin": {
        "listen": ":2019"
    },
    "logging": {
        "logs": {
            "default": {
                "writer": {
                    "output": "stdout"
                },
                "level": "DEBUG"
            },
            "file_logger": {
                "writer": {
                    "filename": "caddy.log",
                    "output": "file"
                },
                "level": "DEBUG"
            }
        }
    },
    "storage": {
        "module": "file_system",
        "root": "/var/lib/caddy"
    },
    "apps": {
        "http": {
            "servers": {
                "srv0": {
                    "listen": [
                        ":443"
                    ],
                    "routes": [
                        {
                            "match": [
                                {
                                    "host": [
                                        "{env.PRIVATE_IP}"
                                    ]
                                }
                            ],
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "handler": "acme_server"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "terminal": true
                        },
                        {
                            "match": [
                                {
                                    "host": [
                                        "*.inductor-test-jkunzler.link"
                                    ]
                                }
                            ],
                            "handle": [
                                {
                                    "handler": "subroute",
                                    "routes": [
                                        {
                                            "handle": [
                                                {
                                                    "@id": "map",
                                                    "defaults": [
                                                        "unknown"
                                                    ],
                                                    "destinations": [
                                                        "{backend_host}"
                                                    ],
                                                    "handler": "map",
                                                    "mappings": [],
                                                    "source": "{http.request.host}"
                                                },
                                                {
                                                    "backend_hostport": "{backend_host}:443",
                                                    "handler": "vars"
                                                }
                                            ]
                                        },
                                        {
                                            "handle": [
                                                {
                                                    "handler": "subroute",
                                                    "routes": [
                                                        {
                                                            "handle": [
                                                                {
                                                                    "body": "App not found.",
                                                                    "handler": "static_response",
                                                                    "status_code": 404
                                                                }
                                                            ]
                                                        }
                                                    ]
                                                }
                                            ],
                                            "match": [
                                                {
                                                    "expression": "{backend_host} == \"unknown\""
                                                }
                                            ]
                                        },
                                        {
                                            "handle": [
                                                {
                                                    "handler": "reverse_proxy",
                                                    "headers": {
                                                        "request": {
                                                            "set": {
                                                                "Host": [
                                                                    "{http.reverse_proxy.upstream.hostport}"
                                                                ]
                                                            }
                                                        }
                                                    },
                                                    "transport": {
                                                        "protocol": "http",
                                                        "tls": {}
                                                    },
                                                    "upstreams": [
                                                        {
                                                            "dial": "{http.vars.backend_hostport}"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ],
                            "terminal": true
                        }
                    ],
                    "errors": {
                        "routes": [
                            {
                                "match": [
                                    {
                                        "host": [
                                            "*.inductor-test-jkunzler.link"
                                        ]
                                    }
                                ],
                                "handle": [
                                    {
                                        "handler": "subroute",
                                        "routes": [
                                            {
                                                "handle": [
                                                    {
                                                        "body": "Reverse Proxy Internal Error.",
                                                        "handler": "static_response",
                                                        "status_code": 500
                                                    }
                                                ]
                                            }
                                        ]
                                    }
                                ],
                                "terminal": true
                            }
                        ]
                    }
                }
            }
        },
        "tls": {
            "automation": {
                "policies": [
                    {
                        "subjects": [
                            "{env.PRIVATE_IP}"
                        ],
                        "issuers": [
                            {
                                "module": "internal"
                            }
                        ]
                    },
                    {
                        "subjects": [
                            "*.inductor-test-jkunzler.link"
                        ],
                        "issuers": [
                            {
                                "ca": "https://acme-staging-v02.api.letsencrypt.org/directory",
                                "challenges": {
                                    "dns": {
                                        "provider": {
                                            "name": "route53"
                                        }
                                    }
                                },
                                "module": "acme"
                            },
                            {
                                "ca": "https://acme-staging-v02.api.letsencrypt.org/directory",
                                "challenges": {
                                    "dns": {
                                        "provider": {
                                            "name": "route53"
                                        }
                                    }
                                },
                                "module": "zerossl"
                            }
                        ]
                    }
                ]
            }
        }
    }
}

Backend Services that the Reverse Proxy forwards request to:

{
	# Global Options.
	debug
	storage file_system {
		root /var/lib/caddy
	}
	log {
		output stdout
	}
	log file_logger {
		output file caddy.log
	}
	# Note that the CA is not specified in the global options due to the
	# following behavior:
	# https://caddy.community/t/local-https-using-local-acme-endpoint/20062/5
}

# Forward to App
{$PRIVATE_IP} {
	reverse_proxy :8000
	# Use the Agent ACME Server.
	tls {
		ca https://{$REVERSE_PROXY_PRIVATE_IP}/acme/local/directory
		ca_root /home/app_agent/root.crt
	}
}

# Forward to Agent
{$PRIVATE_IP}:{$AGENT_ACCESS_PORT} {
	reverse_proxy :{$AGENT_INTERNAL_PORT}
	# Use the Agent ACME Server.
	tls {
		ca https://{$REVERSE_PROXY_PRIVATE_IP}/acme/local/directory
		ca_root /home/app_agent/root.crt
	}
}
1 Like

I strongly suggest running Caddy as a service. See the docs for those recommendations.

Running with caddy start doesn’t do anything to ensure Caddy stays running. It’s meant to be used for quickly spinning up a server in the background for development etc.

I think the problem you’re having is Caddy starts before the system has fully loaded. Running as a service should ensure things start up in the correct order.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.