Caddy in Docker on Synology NAS

1. Caddy version (caddy version):

caddy:latest

2. How I run Caddy:

Trying to run it as a reverse proxy to jellyfin hosted on my Synology NAS.

a. System environment:

Docker

d. My complete Caddyfile or JSON config:

mydomain.ddns.net:80 {
  respond "404" 403
}

mydomain.ddns.net:443 {
  respond "404" 403
}

mydomain.ddns.net:1337 {
  reverse_proxy 127.0.0.1:8096
}

3. The problem I’m having:

I haven’t used Docker much so I’m unsure on how to set this up properly. I currently have a reverse proxy set up on my home PC but I’m migrating everything to a NAS.
I am using the official caddy image, and I’ve given it mount points /data and /config. I’m unsure on where to place the Caddyfile though. I have created a mount point for /etc and put it in the file etc/caddy/Caddyfile but it doesn’t seem to be working.

4. Error messages and/or full log output:

I just noticed after posting this I am actually getting some errors as below:


5. What I already tried:

I’ve tried moving the caddyfile around in my NAS and I’m not sure how to get it to read it from Docker. It’s possible I’m missing something basic since I haven’t used Docker before a day ago.

6. Links to relevant resources:

I don’t have Synology or anything, but make sure it has a standard certificate trust store installed.

I have Synology with few containers running, one of which is Caddy. I’m mobile now, so I’ll come back later to this thread to share details and answer how to set it up.

2 Likes

The way I manage containers’ data is by namespacing them into directories. I have a root directory called /docker in which all containers’ data live. I created a directory inside it, so the path becomes /docker/caddy. Caddy’s files/directories are laid out there. Thus I end up with:

  • /docker/caddy/config
  • /docker/caddy/data
  • /docker/caddy/etc/caddy ← put your Caddyfile here

When you launch a new container, map the directories (not files; there are 2 buttons) as follows (the entries are in the format: local => container:

  • /docker/caddy/config => /config
  • /docker/caddy/data => /data
  • /docker/caddy/etc/caddy => /etc/caddy

I don’t know if you’ve had these issues, but the base Synology OS owns the ports 80 and 443, so your HTTP and HTTPS ports needs to be mapped to different ports. My Caddyfile is something like this:

{
    http_port 8080
    https_port 8443
}
my-domain-name {
    reverse_proxy <IP>:<PORT>
}

Again, you have to map the ports when you’re launching the new container.
I’d include screenshots, but the Synology and I are in different cities and it isn’t exposed externally (yet).

2 Likes

Hi, thanks for the reply. Do I need to forward those extra ports on my router? I’m at work at the moment so can’t test until I get home but just curious if I should do this also.

On your router you need to forward 80 and 443 to 8080 and 8443.

2 Likes

I believe I’ve done this but I’m getting ‘trying to solve challenge’ errors, which I believe relates to not being able to access the ports. On my router I’ve tried this:

External Port: 8080
Internal IP: Internal IP of my NAS
Internal Port: 80
TCP and UDP

External Port: 8443
Internal IP: Internal IP of my NAS
Internal Port: 8443
TCP and UDP

I’ve also tried changing external and IP ports around but I still get the same error.

My caddyfile is as below now:

{
    http_port 8080
    https_port 8443
}

domain.ddns.net:80 {
  respond "404" 403
}

domain.ddns.net:443 {
  respond "404" 403
}

domain.ddns.net:5858 {
  reverse_proxy 127.0.0.1:8096
}

Would I have to enable the NAS firewall and then allow these ports through? Or is the router level enough?

I’ve also added my server/details to the NAS in the external access → DDNS area, I’m assuming this will keep the external IP address up to date.

Swap the external and internal ports here, internal is 8080 and external is 80, and the same for the other.

You can remove the :80 and :443 here and reduce the two blocks to 1.

2 Likes

Hmm I’ve done that and I still get connection refused on all ports. These are all the ports I have: https://i.imgur.com/1n5ALeu.png

Name : External Port : Internal IP : Internal Port

I’ve tried turning the external/internal 80 and 443 ports off and on, and leaving the ones with 443:8443/80:8080 on but no matter what combination of those I get I still get connection refused on all ports. It definitely picks up my IP address externally but just refuses to connect. Anything else I can try?

Hm, it isn’t clear what’s wrong. Are you sure DNS is configured correctly pointing to your external IP address? Can you run curl -v?

Oh, this!

You might want to forward the port for this one too.

curl -v does find my host IP address: (I’ve replaced my domain and IP address)

* Rebuilt URL to: https://mydomain.ddns.net/
*   Trying MY HOST IP...
* TCP_NODELAY set
* Connected to mydomain.ddns.net (MY HOST IP) port 443 (#0)
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 189 bytes...
* schannel: sent initial handshake data: sent 189 bytes
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: encrypted data got 1414
* schannel: encrypted data buffer: offset 1414 length 4096
* schannel: next InitializeSecurityContext failed: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.
* Closing connection 0
* schannel: shutting down SSL/TLS connection with mydomain.ddns.net port 443
* schannel: clear security context handle
curl: (77) schannel: next InitializeSecurityContext failed: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.

And yes I have got that port forwarded to my NAS IP, still unable to connect :frowning:

This:

Means the Synology is hijacking the http call and supplying its own cert. The port forwarding table shows you’re forwarding 443 to both 443 and 8443. Keep only the latter (80 to 8080, and 443 to 8443).

I’m getting this now:

* Rebuilt URL to: https://mydomain.ddns.net/
*   Trying MY IP...
* TCP_NODELAY set
* connect to MY IP port 443 failed: Connection refused
* Failed to connect to mydomain.ddns.net port 443: Connection refused
* Closing connection 0
curl: (7) Failed to connect to mydomain.ddns.net port 443: Connection refused

Which makes me think that maybe my NAS is blocking the port or something? Do I need to enable the NAS firewall and then allow the ports? Or maybe do I need to do something in router configuration on the external access tab of control panel? Currently the only thing in that entire area is my noIP account/URL

Can you recap the current setup? What’s your port forwarding config now? I presume the Caddyfile hasn’t changed.

So I have my Jellyfin server set up on Docker, which I can access using my NAS’ internal IP and the jellyfin port.
Then I have the no-ip account set up like this:

Caddy set up on Docker:

I’ve just noticed that this has port settings on here, maybe this has something to do with it? Or does the caddyfile override it?

And then my caddyfile is this:

{
    http_port 8080
    https_port 8443
}

mydomain.ddns.net:80 {
  respond "404" 403
}

mydomain.ddns.net:443 {
  respond "404" 403
}

mydomain.ddns.net:5858 {
  reverse_proxy 127.0.0.1:8096
}

I know you said I could combine the 80 and 443 blocks but I’m not sure exactly how to do that.

The Caddyfile has nothing to do with Docker port mappings. You need to make sure the Docker container has the right ports mapped. The Caddyfile only controls what Caddy does, inside the container. It cannot have any effect of what happens outside the container.

Your Caddyfile doesn’t really make sense. You’re exposing container ports 80 and 443, so those are the ports on which Caddy will accept requests. Your Caddyfile can probably simply be this:

mydomain.ddns.net {
  reverse_proxy 127.0.0.1:8096
}

Make sure that port 80 and 443 from outside your network eventually get routed to ports 80 and 443 on the container. If you need to use ports 8080 and 8443 at your router and on your machine before it reaches the container because your machine already uses ports 80/443, that’s also probably fine. But it’s important that ports 80/443 are used so that ACME challenges properly succeed.

2 Likes

I’ve played around with a few caddyfiles, and even re-created the caddy container and tried different ports, none have worked, using the caddyfile above I am getting the below now though:

C:\Users\USER>curl -v https://mydomain.ddns.net
* Rebuilt URL to: https://mydomain.ddns.net/
*   Trying MY IP...
* TCP_NODELAY set
* Connected to mydomain.ddns.net (MY IP) port 443 (#0)
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 189 bytes...
* schannel: sent initial handshake data: sent 189 bytes
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: failed to receive handshake, SSL/TLS connection failed
* Closing connection 0
* schannel: shutting down SSL/TLS connection with mydomain.ddns.net port 443
* Send failure: Connection was aborted
* schannel: failed to send close msg: Failed sending data to the peer (bytes written: -1)
* schannel: clear security context handle
curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed

C:\Users\USER>curl -v https://mydomain.ddns.net:80
* Rebuilt URL to: https://mydomain.ddns.net:80/
*   Trying MY IP...
* TCP_NODELAY set
* Connected to mydomain.ddns.net (MY IP) port 80 (#0)
* schannel: SSL/TLS connection with mydomain.ddns.net port 80 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 189 bytes...
* schannel: sent initial handshake data: sent 189 bytes
* schannel: SSL/TLS connection with mydomain.ddns.net port 80 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with mydomain.ddns.net port 80 (step 2/3)
* schannel: encrypted data got 103
* schannel: encrypted data buffer: offset 103 length 4096
* schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid
* Closing connection 0
* schannel: shutting down SSL/TLS connection with mydomain.ddns.net port 80
* schannel: clear security context handle
curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid

C:\Users\USER>curl -v https://mydomain.ddns.net:443
* Rebuilt URL to: https://mydomain.ddns.net:443/
*   Trying MY IP...
* TCP_NODELAY set
* Connected to mydomain.ddns.net (MY IP) port 443 (#0)
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 189 bytes...
* schannel: sent initial handshake data: sent 189 bytes
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: failed to receive handshake, SSL/TLS connection failed
* Closing connection 0
* schannel: shutting down SSL/TLS connection with mydomain.ddns.net port 443
* Send failure: Connection was aborted
* schannel: failed to send close msg: Failed sending data to the peer (bytes written: -1)
* schannel: clear security context handle
curl: (35) schannel: failed to receive handshake, SSL/TLS connection failed

I’ve cleaned up all the other ports I used for my working Windows 10 reverse proxy so it is just this now:

And the logs in the Caddy container are giving me this:

2021/07/04 11:34:21.558	ERROR	tls.issuance.acme	looking up info for HTTP challenge	{"host": "mydomain.ddns.net", "error": "no information found to solve challenge for identifier: mydomain.ddns.net"}

My container has these ports also, not sure if having this means I don’t need to set the http/s port in the file though:

Do you have more than 1 Caddy container running? Are there any stale files in the mounted /data directory?

I did actually have one running on my Windows 10 machine, I’ve turned it off and can access the site on 443 and 80 now, and get my responds that are in the Caddyfile, however I get connection refused on the port I am trying to reverse proxy to. I’ve tried changing the port a few times and no matter what it is I get connection refused, but a connection on 443 and 80. I’ve tried forwarding the ports in the container but that also doesn’t help
https://i.imgur.com/n5EBksb.png

And also tried changing the reverse proxy block to the internal IP instead of 127.0.0.1 and I get connection refused

My current caddyfile is as below:

{
    http_port 8080
    https_port 8443
}

mydomain.ddns.net:80 {
  respond "404" 403
}

mydomain.ddns.net:443 {
  respond "404" 403
}

mydomain.ddns.net:5858 {
  reverse_proxy 127.0.0.1:8096
}

And below is the response I get using curl:

C:\Users\USER>curl -v https://mydomain.ddns.net
* Rebuilt URL to: https://mydomain.ddns.net/
*   Trying myip...
* TCP_NODELAY set
* Connected to mydomain.ddns.net (myip) port 443 (#0)
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 189 bytes...
* schannel: sent initial handshake data: sent 189 bytes
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: encrypted data got 4096
* schannel: encrypted data buffer: offset 4096 length 4096
* schannel: encrypted data length: 197
* schannel: encrypted data buffer: offset 197 length 4096
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: encrypted data got 448
* schannel: encrypted data buffer: offset 645 length 4096
* schannel: sending next handshake data: sending 93 bytes...
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 2/3)
* schannel: encrypted data got 195
* schannel: encrypted data buffer: offset 195 length 4096
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with mydomain.ddns.net port 443 (step 3/3)
* schannel: stored credential handle in session cache
> GET / HTTP/1.1
> Host: mydomain.ddns.net
> User-Agent: curl/7.55.1
> Accept: */*
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 129
* schannel: encrypted data buffer: offset 129 length 103424
* schannel: decrypted data length: 100
* schannel: decrypted data added: 100
* schannel: decrypted data cached: offset 100 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 100 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 100
* schannel: decrypted data buffer: offset 0 length 102400
< HTTP/1.1 403 Forbidden
< Server: Caddy
< Date: Tue, 13 Jul 2021 10:26:47 GMT
< Content-Length: 3
<
404* Connection #0 to host mydomain.ddns.net left intact

C:\Users\USER>curl -v https://mydomain.ddns.net:5858
* Rebuilt URL to: https://mydomain.ddns.net:5858/
*   Trying myip...
* TCP_NODELAY set
* connect to myip port 5858 failed: Connection refused
* Failed to connect to mydomain.ddns.net port 5858: Connection refused
* Closing connection 0
curl: (7) Failed to connect to mydomain.ddns.net port 5858: Connection refused