1. The problem I’m having:
I would like to have reverse proxy working for my services (proxmox, jellyfin, etc) and access from domain name instead of IP Address and proxy without exposing any ports.
I have created A name record in my cloudflare as follows
I have set up Caddy using docker containers. Test it using gitea containers and I expect can access gitea through git.brewx.my.id
However, it does not work with following caddy logs
2. Error messages and/or full log output:
pi@rasppi:~/containers/caddy_project/caddy $ docker logs caddy
{"level":"info","ts":1710918444.5609567,"logger":"docker-proxy","msg":"Running caddy proxy server"}
{"level":"info","ts":1710918444.565335,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1710918444.565869,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1710918444.5659153,"logger":"docker-proxy","msg":"Running caddy proxy controller"}
{"level":"info","ts":1710918444.5697014,"logger":"docker-proxy","msg":"Start","CaddyfilePath":"","EnvFile":"","LabelPrefix":"caddy","PollingInterval":30,"ProxyServiceTasks":true,"ProcessCaddyfile":true,"ScanStoppedContainers":true,"IngressNetworks":"[]","DockerSockets":[""],"DockerCertsPath":[""],"DockerAPIsVersion":[""]}
{"level":"info","ts":1710918444.5751677,"logger":"docker-proxy","msg":"Connecting to docker events","DockerSocket":""}
{"level":"info","ts":1710918444.576002,"logger":"docker-proxy","msg":"Caddy ContainerID","ID":"02ea27fb49ed5b0a210a2ecd39bb7d680311be6a720a34d6f3f5a6dc0d010b85"}
{"level":"info","ts":1710918444.589329,"logger":"docker-proxy","msg":"IngressNetworksMap","ingres":"map[8a5cc6a3aeb032c4848382bd118d424a749131680235e00df1804144b50bd99c:true caddy-network:true]"}
{"level":"info","ts":1710918444.6207874,"logger":"docker-proxy","msg":"Swarm is available","new":false}
{"level":"info","ts":1710918444.6453142,"logger":"docker-proxy","msg":"New Caddyfile","caddyfile":"*.brewx.my.id {\n\t@vaultwarden host vault.brewx.my.id\n\t@gitea host git.brewx.my.id\n\thandle @vaultwarden {\n\t\treverse_proxy :80\n\t}\n\thandle @gitea {\n\t\treverse_proxy 172.18.0.3:3000\n\t}\n\tencode gzip\n\theader {\n\t\t-Last-Modified\n\t\t-Server\n\t\t-X-Powered-By\n\t\tStrict-Transport-Security max-age=31536000;\n\t\tX-Content-Type-Options nosniff\n\t\tX-Frame-Options SAMEORIGIN\n\t\tX-Robots-Tag noindex, nofollow\n\t\tX-XSS-Protection 1; mode=block\n\t}\n}\n"}
{"level":"info","ts":1710918444.6495988,"logger":"docker-proxy","msg":"New Config JSON","json":"{\"apps\":{\"http\":{\"servers\":{\"srv0\":{\"listen\":[\":443\"],\"routes\":[{\"match\":[{\"host\":[\"*.brewx.my.id\"]}],\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"headers\",\"response\":{\"deferred\":true,\"delete\":[\"Last-Modified\",\"Server\",\"X-Powered-By\"],\"replace\":{\"X-Robots-Tag\":[{\"replace\":\"nofollow\",\"search_regexp\":\"noindex,\"}],\"X-XSS-Protection\":[{\"replace\":\"mode=block\",\"search_regexp\":\"1;\"}]},\"set\":{\"Strict-Transport-Security\":[\"max-age=31536000;\"],\"X-Content-Type-Options\":[\"nosniff\"],\"X-Frame-Options\":[\"SAMEORIGIN\"]}}},{\"encodings\":{\"gzip\":{}},\"handler\":\"encode\",\"prefer\":[\"gzip\"]}]},{\"group\":\"group2\",\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\":80\"}]}]}]}],\"match\":[{\"host\":[\"vault.brewx.my.id\"]}]},{\"group\":\"group2\",\"handle\":[{\"handler\":\"subroute\",\"routes\":[{\"handle\":[{\"handler\":\"reverse_proxy\",\"upstreams\":[{\"dial\":\"172.18.0.3:3000\"}]}]}]}],\"match\":[{\"host\":[\"git.brewx.my.id\"]}]}]}],\"terminal\":true}]}}}}}"}
{"level":"info","ts":1710918444.6498141,"logger":"docker-proxy","msg":"Sending configuration to","server":"localhost"}
{"level":"info","ts":1710918444.6525726,"logger":"admin.api","msg":"received request","method":"POST","host":"localhost:2019","uri":"/load","remote_ip":"127.0.0.1","remote_port":"53360","headers":{"Accept-Encoding":["gzip"],"Content-Length":["1021"],"Content-Type":["application/json"],"User-Agent":["Go-http-client/1.1"]}}
{"level":"info","ts":1710918444.6563332,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
{"level":"info","ts":1710918444.657117,"logger":"http.auto_https","msg":"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}
{"level":"info","ts":1710918444.6571681,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x400078a600"}
{"level":"info","ts":1710918444.6572075,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
{"level":"info","ts":1710918444.660419,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
{"level":"info","ts":1710918444.660871,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
{"level":"info","ts":1710918444.661576,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
{"level":"info","ts":1710918444.661926,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
{"level":"info","ts":1710918444.661974,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["*.brewx.my.id"]}
{"level":"info","ts":1710918444.6647954,"logger":"tls.obtain","msg":"acquiring lock","identifier":"*.brewx.my.id"}
{"level":"info","ts":1710918444.666001,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
{"level":"info","ts":1710918444.6660635,"logger":"admin.api","msg":"load complete"}
{"level":"info","ts":1710918444.6675334,"logger":"docker-proxy","msg":"Successfully configured","server":"localhost"}
{"level":"info","ts":1710918444.6740205,"logger":"admin","msg":"stopped previous server","address":"localhost:2019"}
{"level":"info","ts":1710918444.677343,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"}
{"level":"info","ts":1710918444.6799412,"logger":"tls","msg":"finished cleaning storage units"}
{"level":"info","ts":1710918444.6804833,"logger":"tls.obtain","msg":"lock acquired","identifier":"*.brewx.my.id"}
{"level":"info","ts":1710918444.681314,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"*.brewx.my.id"}
{"level":"info","ts":1710918446.0278885,"logger":"http","msg":"waiting on internal rate limiter","identifiers":["*.brewx.my.id"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"info","ts":1710918446.027991,"logger":"http","msg":"done waiting on internal rate limiter","identifiers":["*.brewx.my.id"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
{"level":"error","ts":1710918446.8841522,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"*.brewx.my.id","issuer":"acme-v02.api.letsencrypt.org-directory","error":"[*.brewx.my.id] solving challenges: *.brewx.my.id: no solvers available for remaining challenges (configured=[http-01 tls-alpn-01] offered=[dns-01] remaining=[dns-01]) (order=https://acme-v02.api.letsencrypt.org/acme/order/1627876817/253835023237) (ca=https://acme-v02.api.letsencrypt.org/directory)"}
{"level":"warn","ts":1710918446.8846185,"logger":"http","msg":"missing email address for ZeroSSL; it is strongly recommended to set one for next time"}
{"level":"info","ts":1710918448.6534657,"logger":"http","msg":"generated EAB credentials","key_id":"CONfkdYZhl-JAsr7_bpHQA"}
3. Caddy version:
Docker containers-alpine
4. How I installed and ran Caddy:
a. System environment:
Hardware : Raspberry Pi 4B
OS : Raspberry Pi OS Lite 64bit
Installing the caddy in docker container
b. Command:
PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.
c. Service/unit/compose file:
services:
docker-proxy:
build:
context: .
dockerfile: Dockerfile
container_name: caddy
restart: unless-stopped
env_file: .env
ports:
- 80:80
- 443:443
networks:
- caddy-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data/caddy
- ./config:/config/caddy
deploy:
labels:
caddy.email: "email@gmail.com"
caddy: "*.brewx.my.id"
caddy.tls.dns: "cloudflare $CF_API_TOKEN"
volumes:
data: {}
networks:
caddy-network:
external: true
d. My complete Caddy config:
Dockerfile :
FROM --platform=linux/arm64/v8 caddy:builder-alpine AS builder
RUN xcaddy build \
--with github.com/lucaslorentz/caddy-docker-proxy/v2 \
--with github.com/caddy-dns/cloudflare
FROM caddy:alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
CMD ["caddy", "docker-proxy"]
.env file
# Cloudflare API token should be scoped:
# - Zone.Zone: Read
# - Zone.DNS: Edit
CF_API_TOKEN="PnBBlbHIDDENA6KaCt"
I tested it using gitea container with following compose file
services:
server:
image: gitea/gitea:latest
container_name: gitea
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
networks:
- caddy-network
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
labels:
caddy: "*.brewx.my.id"
caddy.2_handle: "@gitea"
caddy.2_@gitea: "host git.brewx.my.id"
caddy.2_handle.reverse_proxy: "{{upstreams 3000}}"
caddy_2_handle.reverse_proxy_0: "{{upstreams 2222}}"
networks:
caddy-network:
external: true