Custom Caddy Configuration in AIO

Some or all of the below information will be requested if it isn’t supplied; for fastest response please provide as much as you can. :heart:

The Basics

  • Nextcloud Server version (e.g., 29.x.x):

  • Operating system and version (e.g., Ubuntu 24.04):

    • Nextcloud AIO v11.4.0
  • Installation method (e.g. AlO, NCP, Bare Metal/Archive, etc.)

    • AIO on openmediavault
  • Are you using CloudfIare, mod_security, or similar? (Yes / No)

    • no

Summary of the issue you are facing:

Hello,

I’ve added Caddy as community-container to AIO. Now, I wanted to add a custom configuration file to achieve the following:

Within my home network, I want Docker containers—which I currently access directly via 192.168.xxx.xxx and their respective ports—to be reachable via URLs. DNS resolution in my network is handled by AdGuard. My Nextcloud instance is accessible via xxx.duckdns.org.

My approach was as follows, using the Immich container as an example (currently only reachable at 192.168.xxx.xxx:2283):

  1. In AdGuard, I set up a DNS rewrite for immich.xxx.duckdns.org pointing to 192.168.xxx.xxx (the IP where port 443 is exposed).
  2. In the file empty under /data/caddy-imports, I added the following entry:
immich.xxx.duckdns.org {
    reverse_proxy nextcloud-aio-apache:2283
}

This approach didn’t work.
I get an ERR_SSL_PROTOCOL_ERROR in the browser.

I’d appreciate any hints on what might be causing the issue. Is the Caddy file incorrectly configured? Is my desired setup even possible?

Additionally, would it be possible to forward URLs from domains other than my Nextcloud domain to a specific port (e.g., xxx.domain.com to port 8085)?

Thanks in advance!

Log entries

2025-07-20T18:59:19.865050526Z Connection to nextcloud-aio-nextcloud (172.21.0.13) 9001 port [tcp/*] succeeded!
2025-07-20T18:59:19.865387385Z + '[' -f /nextcloud/admin/files/nextcloud-aio-caddy/allowed-countries.txt ']'
2025-07-20T18:59:19.874054261Z ++ dig nextcloud-aio-caddy A +short +search
2025-07-20T18:59:19.874080021Z ++ head -1
2025-07-20T18:59:19.888585819Z + IPv4_ADDRESS=172.21.0.4
2025-07-20T18:59:19.889593454Z ++ echo 172.21.0.4
2025-07-20T18:59:19.889636328Z ++ sed 's|[0-9]\+$|0/16|'
2025-07-20T18:59:19.890345421Z + IPv4_ADDRESS=172.21.0.0/16
2025-07-20T18:59:19.890797950Z ++ sed 's|trusted_proxies.*|trusted_proxies static 172.21.0.0/16|' /Caddyfile
2025-07-20T18:59:19.892262432Z + CADDYFILE='{
2025-07-20T18:59:19.892297633Z     auto_https disable_redirects
2025-07-20T18:59:19.892305917Z 
2025-07-20T18:59:19.892312178Z     storage file_system {
2025-07-20T18:59:19.892318202Z         root /data/caddy
2025-07-20T18:59:19.892324019Z     }
2025-07-20T18:59:19.892329650Z 
2025-07-20T18:59:19.892335387Z     servers {
2025-07-20T18:59:19.892341531Z         trusted_proxies static 172.21.0.0/16
2025-07-20T18:59:19.892348309Z     }
2025-07-20T18:59:19.892354284Z 
2025-07-20T18:59:19.892369877Z     log {
2025-07-20T18:59:19.892376079Z         level ERROR
2025-07-20T18:59:19.892381854Z     }
2025-07-20T18:59:19.892402349Z }
2025-07-20T18:59:19.892408566Z 
2025-07-20T18:59:19.892414337Z (GEOFILTER) {
2025-07-20T18:59:19.892419528Z     @geofilter {
2025-07-20T18:59:19.892424750Z         not maxmind_geolocation {
2025-07-20T18:59:19.892439058Z             db_path "/data/GeoLite2-Country.mmdb"
2025-07-20T18:59:19.892445752Z             allow_countries
2025-07-20T18:59:19.892451255Z         }
2025-07-20T18:59:19.892456465Z         not remote_ip private_ranges
2025-07-20T18:59:19.892461710Z         # Exclude IP-address of scan.nexcloud.com so that scanning still works
2025-07-20T18:59:19.892467328Z         not remote_ip 95.217.53.149
2025-07-20T18:59:19.892473139Z     }
2025-07-20T18:59:19.892478383Z     respond @geofilter "Access denied" 403 {
2025-07-20T18:59:19.892484131Z 		close
2025-07-20T18:59:19.892489595Z 	}
2025-07-20T18:59:19.892494636Z }
2025-07-20T18:59:19.892499554Z 
2025-07-20T18:59:19.892504680Z https://{$NC_DOMAIN}:443 {
2025-07-20T18:59:19.892517563Z     # import GEOFILTER
2025-07-20T18:59:19.892523210Z     reverse_proxy nextcloud-aio-apache:{$APACHE_PORT}
2025-07-20T18:59:19.892528903Z 
2025-07-20T18:59:19.892534121Z     # TLS options
2025-07-20T18:59:19.892539305Z     tls {
2025-07-20T18:59:19.892544520Z         issuer acme {
2025-07-20T18:59:19.892549547Z             disable_http_challenge
2025-07-20T18:59:19.892554621Z         }
2025-07-20T18:59:19.892559569Z     }
2025-07-20T18:59:19.892564438Z }'
2025-07-20T18:59:19.892832599Z + echo '{
2025-07-20T18:59:19.892847667Z     auto_https disable_redirects
2025-07-20T18:59:19.892868312Z 
2025-07-20T18:59:19.892876251Z     storage file_system {
2025-07-20T18:59:19.892883413Z         root /data/caddy
2025-07-20T18:59:19.892890027Z     }
2025-07-20T18:59:19.892896688Z 
2025-07-20T18:59:19.892902948Z     servers {
2025-07-20T18:59:19.892909638Z         trusted_proxies static 172.21.0.0/16
2025-07-20T18:59:19.892916564Z     }
2025-07-20T18:59:19.892923033Z 
2025-07-20T18:59:19.892929355Z     log {
2025-07-20T18:59:19.892946076Z         level ERROR
2025-07-20T18:59:19.892953138Z     }
2025-07-20T18:59:19.892959799Z }
2025-07-20T18:59:19.892966522Z 
2025-07-20T18:59:19.892972688Z (GEOFILTER) {
2025-07-20T18:59:19.892978958Z     @geofilter {
2025-07-20T18:59:19.892985782Z         not maxmind_geolocation {
2025-07-20T18:59:19.893002771Z             db_path "/data/GeoLite2-Country.mmdb"
2025-07-20T18:59:19.893019652Z             allow_countries
2025-07-20T18:59:19.893026750Z         }
2025-07-20T18:59:19.893033610Z         not remote_ip private_ranges
2025-07-20T18:59:19.893040173Z         # Exclude IP-address of scan.nexcloud.com so that scanning still works
2025-07-20T18:59:19.893047234Z         not remote_ip 95.217.53.149
2025-07-20T18:59:19.893054129Z     }
2025-07-20T18:59:19.893060812Z     respond @geofilter "Access denied" 403 {
2025-07-20T18:59:19.893067588Z 		close
2025-07-20T18:59:19.893073903Z 	}
2025-07-20T18:59:19.893079930Z }
2025-07-20T18:59:19.893094877Z 
2025-07-20T18:59:19.893102254Z https://{$NC_DOMAIN}:443 {
2025-07-20T18:59:19.893108580Z     # import GEOFILTER
2025-07-20T18:59:19.893114981Z     reverse_proxy nextcloud-aio-apache:{$APACHE_PORT}
2025-07-20T18:59:19.893122202Z 
2025-07-20T18:59:19.893128303Z     # TLS options
2025-07-20T18:59:19.893134795Z     tls {
2025-07-20T18:59:19.893141183Z         issuer acme {
2025-07-20T18:59:19.893147434Z             disable_http_challenge
2025-07-20T18:59:19.893154044Z         }
2025-07-20T18:59:19.893160479Z     }
2025-07-20T18:59:19.893176723Z }'
2025-07-20T18:59:19.893451398Z ++ head -n 1 /nextcloud/admin/files/nextcloud-aio-caddy/allowed-countries.txt
2025-07-20T18:59:19.894120668Z + ALLOW_CONTRIES=
2025-07-20T18:59:19.894683897Z + echo ''
2025-07-20T18:59:19.895004887Z + grep -q '^[A-Z ]\+$'
2025-07-20T18:59:19.895883601Z + '[' -f /nextcloud/admin/files/nextcloud-aio-caddy/GeoLite2-Country.mmdb ']'
2025-07-20T18:59:19.896070888Z + '[' -f /nextcloud/admin/files/nextcloud-aio-caddy/block-vaultwarden-admin ']'
2025-07-20T18:59:19.896496524Z ++ dig A +short nextcloud-aio-vaultwarden
2025-07-20T18:59:20.848417741Z + '[' -n '' ']'
2025-07-20T18:59:20.849180957Z ++ dig A +short nextcloud-aio-stalwart
2025-07-20T18:59:21.845763221Z + '[' -n '' ']'
2025-07-20T18:59:21.846145231Z ++ dig A +short nextcloud-aio-lldap
2025-07-20T18:59:22.846985236Z + '[' -n '' ']'
2025-07-20T18:59:22.847343046Z ++ dig A +short nextcloud-aio-nocodb
2025-07-20T18:59:23.595697649Z + '[' -n '' ']'
2025-07-20T18:59:23.595760786Z + nc -z -w 30 host.docker.internal 8096
2025-07-20T18:59:23.605890303Z ++ dig A +short nextcloud-aio-jellyseerr
2025-07-20T18:59:24.345759509Z + '[' -n '' ']'
2025-07-20T18:59:24.345792375Z + mkdir -p /data/caddy-imports
2025-07-20T18:59:24.354502561Z + grep -q /data/caddy-imports /Caddyfile
2025-07-20T18:59:24.355214662Z + echo 'import /data/caddy-imports/*'
2025-07-20T18:59:24.355268446Z + echo '# empty file so that caddy does not print a warning'
2025-07-20T18:59:24.355541952Z + '[' '' = 1 ']'
2025-07-20T18:59:24.355855890Z ++ sed 's|  import GEOFILTER|# import GEOFILTER|' /Caddyfile
2025-07-20T18:59:24.356572957Z + CADDYFILE='{
2025-07-20T18:59:24.356584443Z     auto_https disable_redirects
2025-07-20T18:59:24.356590783Z 
2025-07-20T18:59:24.356596399Z     storage file_system {
2025-07-20T18:59:24.356602044Z         root /data/caddy
2025-07-20T18:59:24.356607656Z     }
2025-07-20T18:59:24.356613279Z 
2025-07-20T18:59:24.356618727Z     servers {
2025-07-20T18:59:24.356624544Z         trusted_proxies static 172.21.0.0/16
2025-07-20T18:59:24.356630371Z     }
2025-07-20T18:59:24.356635904Z 
2025-07-20T18:59:24.356641317Z     log {
2025-07-20T18:59:24.356646832Z         level ERROR
2025-07-20T18:59:24.356652327Z     }
2025-07-20T18:59:24.356657747Z }
2025-07-20T18:59:24.356663072Z 
2025-07-20T18:59:24.356668620Z (GEOFILTER) {
2025-07-20T18:59:24.356674468Z     @geofilter {
2025-07-20T18:59:24.356679913Z         not maxmind_geolocation {
2025-07-20T18:59:24.356685624Z             db_path "/data/GeoLite2-Country.mmdb"
2025-07-20T18:59:24.356691469Z             allow_countries
2025-07-20T18:59:24.356696988Z         }
2025-07-20T18:59:24.356702335Z         not remote_ip private_ranges
2025-07-20T18:59:24.356707963Z         # Exclude IP-address of scan.nexcloud.com so that scanning still works
2025-07-20T18:59:24.356713755Z         not remote_ip 95.217.53.149
2025-07-20T18:59:24.356719515Z     }
2025-07-20T18:59:24.356725005Z     respond @geofilter "Access denied" 403 {
2025-07-20T18:59:24.356730774Z 		close
2025-07-20T18:59:24.356736515Z 	}
2025-07-20T18:59:24.356742082Z }
2025-07-20T18:59:24.356747514Z 
2025-07-20T18:59:24.356752733Z https://{$NC_DOMAIN}:443 {
2025-07-20T18:59:24.356758346Z     # import GEOFILTER
2025-07-20T18:59:24.356764070Z     reverse_proxy nextcloud-aio-apache:{$APACHE_PORT}
2025-07-20T18:59:24.356769845Z 
2025-07-20T18:59:24.356775222Z     # TLS options
2025-07-20T18:59:24.356780731Z     tls {
2025-07-20T18:59:24.356786144Z         issuer acme {
2025-07-20T18:59:24.356791530Z             disable_http_challenge
2025-07-20T18:59:24.356807898Z         }
2025-07-20T18:59:24.356813913Z     }
2025-07-20T18:59:24.356819420Z }
2025-07-20T18:59:24.356824766Z import /data/caddy-imports/*'
2025-07-20T18:59:24.356834596Z + echo '{
2025-07-20T18:59:24.356840892Z     auto_https disable_redirects
2025-07-20T18:59:24.356846428Z 
2025-07-20T18:59:24.356851760Z     storage file_system {
2025-07-20T18:59:24.356857142Z         root /data/caddy
2025-07-20T18:59:24.356862781Z     }
2025-07-20T18:59:24.356868429Z 
2025-07-20T18:59:24.356873754Z     servers {
2025-07-20T18:59:24.356879137Z         trusted_proxies static 172.21.0.0/16
2025-07-20T18:59:24.356884675Z     }
2025-07-20T18:59:24.356890062Z 
2025-07-20T18:59:24.356895210Z     log {
2025-07-20T18:59:24.356900700Z         level ERROR
2025-07-20T18:59:24.356906015Z     }
2025-07-20T18:59:24.356911454Z }
2025-07-20T18:59:24.356916786Z 
2025-07-20T18:59:24.356921938Z (GEOFILTER) {
2025-07-20T18:59:24.356927195Z     @geofilter {
2025-07-20T18:59:24.356932677Z         not maxmind_geolocation {
2025-07-20T18:59:24.356938058Z             db_path "/data/GeoLite2-Country.mmdb"
2025-07-20T18:59:24.356943622Z             allow_countries
2025-07-20T18:59:24.356949077Z         }
2025-07-20T18:59:24.356954354Z         not remote_ip private_ranges
2025-07-20T18:59:24.356959866Z         # Exclude IP-address of scan.nexcloud.com so that scanning still works
2025-07-20T18:59:24.356965431Z         not remote_ip 95.217.53.149
2025-07-20T18:59:24.356970940Z     }
2025-07-20T18:59:24.356976263Z     respond @geofilter "Access denied" 403 {
2025-07-20T18:59:24.356981935Z 		close
2025-07-20T18:59:24.356987698Z 	}
2025-07-20T18:59:24.356993112Z }
2025-07-20T18:59:24.356998415Z 
2025-07-20T18:59:24.357003596Z https://{$NC_DOMAIN}:443 {
2025-07-20T18:59:24.357009195Z     # import GEOFILTER
2025-07-20T18:59:24.357014716Z     reverse_proxy nextcloud-aio-apache:{$APACHE_PORT}
2025-07-20T18:59:24.357020298Z 
2025-07-20T18:59:24.357025577Z     # TLS options
2025-07-20T18:59:24.357030923Z     tls {
2025-07-20T18:59:24.357036663Z         issuer acme {
2025-07-20T18:59:24.357042112Z             disable_http_challenge
2025-07-20T18:59:24.357047546Z         }
2025-07-20T18:59:24.357052870Z     }
2025-07-20T18:59:24.357058141Z }
2025-07-20T18:59:24.357063305Z import /data/caddy-imports/*'
2025-07-20T18:59:24.357079878Z + set +x
2025-07-20T18:59:24.492120000Z {"level":"info","ts":1753037964.4908574,"msg":"maxprocs: Leaving GOMAXPROCS=4: CPU quota undefined"}
2025-07-20T18:59:24.493042302Z {"level":"info","ts":1753037964.4918618,"msg":"GOMEMLIMIT is updated","package":"github.com/KimMachineGun/automemlimit/memlimit","GOMEMLIMIT":14895842918,"previous":9223372036854775807}
2025-07-20T18:59:24.494007225Z {"level":"info","ts":1753037964.4932349,"msg":"using config from file","file":"/Caddyfile"}
2025-07-20T18:59:24.496382238Z {"level":"info","ts":1753037964.4962862,"msg":"adapted config to JSON","adapter":"caddyfile"}
2025-07-20T18:59:24.499218980Z {"level":"info","ts":1753037964.4990833,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 4882 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
2025-07-20T18:59:24.500970672Z {"level":"info","ts":1753037964.5008519,"msg":"serving initial configuration"}

Nextcloud

I’m not familiar with AiO community caddy.. but I would rather add a reverse proxy “in front of” AiO and other applications and not behind/inside AiO. this would be more effective and straight as AiO already ships few reverse proxies (caddy and apache) and hosting the applications behind it would make all traffic flow through all this reverse proxies - not really efficient.

Hi, sorry for not being clear. I’m using the caddy container shipped with AiO. This is what I’m using / trying to :slight_smile:

if you use your reverse proxy in front of all containers

flowchart LR
  fritz.box-- port forward<br>http tcp/80<br>https tcp/443 -->RP;
   fritz.box(router<br>192.168.179.1);
		RP[reverse proxy<br>:80 + :443<br>nc.mydomain.tld<br>collabora.mydomain.tld<br>application1.mydomain.tld<br>application2.mydomain.tld] 
		subgraph nc.mydomain.tld
			NC[NC<br>:80]-->NCDB[(mariadb)];
		end
		subgraph collabora.mydomain.tld

		  CODE[Collabora<br>:9980]
		end
		subgraph application1.mydomain.tld
		  app1[app1<br>:80]-->APP1DB[(postgres)]
		end
		RP--http-->NC & CODE & app1

you must not use AIO destination as reverse proxy directive

but rather you application IP:

immich.xxx.duckdns.org {
    reverse_proxy 192.168.123.123:80
}

or for docker even better the service name (requires same Docker network for Caddy and Immich)

immich.xxx.duckdns.org {
    reverse_proxy app1:80
}

but this question belongs more into the Caddy support..