Troubleshooting Searx Caddy config on a local Docker network

1. Caddy version (caddy version): v2

2. How I run Caddy:

I use caddy in docker as a reverse proxy for a bunch of local self-hosted services also running in docker. I have raspberry pi zero that runs as my DNS server on my local network. All services are only exposed internally. I only expose the services through the caddy container. The services are all on the same dedicated docker network.

a. System environment:

OS: Ubuntu Server 20.04, Docker 20.10.7

b. Command:

docker-compose up

c. Service/unit/compose file:

version: "3.7"

services:
  caddy:
    image: caddy:latest
    container_name: caddy
    hostname: caddy
    restart: unless-stopped
    environment:
      - MY_DOMAIN
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./site:/srv
      - ./data:/data
      - ./config:/config
networks:
  default:
    external:
      name: $DOCKER_MY_NETWORK

d. My complete Caddyfile or JSON config:

paperless.{$MY_DOMAIN} {
  tls internal
  reverse_proxy paperless-app:8000
}

nextcloud.{$MY_DOMAIN} {
  tls internal
  reverse_proxy nextcloud-web:80
  header Strict-Transport-Security max-age=31536000;
  redir /.well-known/carddav /remote.php/carddav 301
  redir /.well-known/caldav /remote.php/caldav 301
}

portainer.{$MY_DOMAIN} {
  tls internal
  reverse_proxy portainer:9000
}

grafana.{$MY_DOMAIN} {
    tls internal
    reverse_proxy grafana:3000
}

prometheus.{$MY_DOMAIN} {
    tls internal
    reverse_proxy prometheus:9090
}

huginn.{$MY_DOMAIN} {
    tls internal
    reverse_proxy huginn-app:3000
}

rss.{$MY_DOMAIN} {
    tls internal
    reverse_proxy miniflux-app:8080
}

search.{$MY_DOMAIN} {
  tls internal
   @api {
         path /config
	       path /status
   }
 
   @static {
         path /static/*
   }
 
   @notstatic {
         not path /static/*
   }
 
   @morty {
         path /morty/*
   }
 
   @notmorty {
         not path /morty/*
   }
 
   header {
         # Enable HTTP Strict Transport Security (HSTS) to force clients to always connect via HTTPS
         Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
 
         # Enable cross-site filter (XSS) and tell browser to block detected attacks
         X-XSS-Protection "1; mode=block"
 
         # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type
         X-Content-Type-Options "nosniff"
 
         # Disallow the site to be rendered within a frame (clickjacking protection)
         X-Frame-Options "SAMEORIGIN"
 
         # Disable some features
         Permissions-Policy "accelerometer=(),ambient-light-sensor=(),autoplay=(),camera=(),encrypted-media=(),focus-without-user-activation=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),speaker=(),sync-xhr=(),usb=(),vr=()"
 
         # Referer
         Referrer-Policy "no-referrer"
 
         # X-Robots-Tag
         X-Robots-Tag "noindex, noarchive, nofollow"
 
         # Remove Server header
         -Server
 
         Connection ""
   }
 
   header @api {
         Access-Control-Allow-Methods "GET, OPTIONS"
         Access-Control-Allow-Origin  "*"
   }
 
   # Cache
   header @static {
         # Cache
         Cache-Control "public, max-age=31536000"
         defer
   }
 
   header @notstatic {
         # No Cache
         Cache-Control "no-cache, no-store"
         Pragma "no-cache"
   }
 
   # CSP (see http://content-security-policy.com/ )
   header @morty {
	       Content-Security-Policy "default-src 'none'; style-src 'self' 'unsafe-inline'; form-action 'self'; frame-ancestors 'self'; base-uri 'self'; img-src 'self' data:; font-src 'self'; frame-src 'self'"
   }
 
   header @notmorty {
         Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self'; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src 'self' data: https://*.tile.openstreetmap.org; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com"
   }
 
   # Morty
   handle @morty {
         reverse_proxy morty-app:3000
   }
 
   # Filtron
   handle {
         encode zstd gzip
 
         reverse_proxy filtron-app:4040 {
                header_up X-Forwarded-Port {http.request.port}
                header_up X-Forwarded-Proto {http.request.scheme}
                header_up X-Forwarded-TlsProto {tls_protocol}
                header_up X-Forwarded-TlsCipher {tls_cipher}
                header_up X-Forwarded-HttpsProto {proto}
         }
   }
}

3. The problem I’m having:

Unable to get the url that uses morty to work. I get a 502 response. Everything else runs flawlessly. I took the caddy setup from GitHub - searx/searx-docker: Create a searx instance using Docker. The difference I can see is that they use the host network and not an internal docker network.

4. Error messages and/or full log output:

Caddy Log:

{"level":"debug","ts":1639343222.1266415,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"morty-app:3000","request":{"remote_addr":"192.168.0.158:53645","proto":"HTTP/2.0","method":"GET","host":"search.vault.lan","uri":"/morty/?mortyurl=https%3A%2F%2Fencrypted-tbn0.gstatic.com%2Fimages%3Fq%3Dtbn%3AANd9GcSSy0oSxa-m7JajX9-dHV2zndWtCGLsatPSdw%26usqp%3DCAU","headers":{"Sec-Ch-Ua-Platform":["\"Windows\""],"Upgrade-Insecure-Requests":["1"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Sec-Fetch-Site":["none"],"Cookie":["categories=general; language=en-US; locale=en; autocomplete=; image_proxy=1; safesearch=0; theme=oscar; results_on_new_tab=0; doi_resolver=oadoi.org; oscar-style=logicodev; simple_style=auto; advanced_search=0; query_in_title=; disabled_engines=; enabled_engines=; disabled_plugins=; enabled_plugins=; tokens=; method=POST"],"X-Forwarded-For":["192.168.0.158"],"Sec-Ch-Ua":["\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br"],"Accept-Language":["en-US,en;q=0.9"],"Pragma":["no-cache"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Dest":["document"],"X-Forwarded-Proto":["https"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"search.vault.lan"}},"duration":0.001728427,"error":"dial tcp 192.168.240.23:3000: connect: connection refused"},
{"level":"error","ts":1639343222.1268082,"logger":"http.log.error","msg":"dial tcp 192.168.240.23:3000: connect: connection refused","request":{"remote_addr":"192.168.0.158:53645","proto":"HTTP/2.0","method":"GET","host":"search.vault.lan","uri":"/morty/?mortyurl=https%3A%2F%2Fencrypted-tbn0.gstatic.com%2Fimages%3Fq%3Dtbn%3AANd9GcSSy0oSxa-m7JajX9-dHV2zndWtCGLsatPSdw%26usqp%3DCAU","headers":{"Sec-Fetch-User":["?1"],"Sec-Fetch-Dest":["document"],"Pragma":["no-cache"],"Cache-Control":["no-cache"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Mode":["navigate"],"Cookie":["categories=general; language=en-US; locale=en; autocomplete=; image_proxy=1; safesearch=0; theme=oscar; results_on_new_tab=0; doi_resolver=oadoi.org; oscar-style=logicodev; simple_style=auto; advanced_search=0; query_in_title=; disabled_engines=; enabled_engines=; disabled_plugins=; enabled_plugins=; tokens=; method=POST"],"Sec-Ch-Ua-Platform":["\"Windows\""],"Upgrade-Insecure-Requests":["1"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Sec-Fetch-Site":["none"],"Sec-Ch-Ua":["\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\""],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"],"Accept-Encoding":["gzip, deflate, br"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"search.vault.lan"}},"duration":0.002263989,"status":502,"err_id":"nitvcq1cc","err_trace":"reverseproxy.statusError (reverseproxy.go:858)"}

5. What I already tried:

I honestly don’t know where to start.

6. Links to relevant resources:

GitHub - searx/searx-docker: Create a searx instance using Docker,

What you have seems fine to me.

Ultimately this is a Docker question rather than a Caddy one, there’s nothing special going on in terms of configuring Caddy, this is a Docker networking issue.

Taking a quick glance at https://hub.docker.com/r/dalf/morty though, it seems like the default listen address for morty is 127.0.0.1:3000 which means it only accepts connections from inside the container, I think. So instead, you might need to it with MORTY_ADDRESS=0.0.0.0:3000 to make it listen on all addresses.

You only showed your docker-compose.yml for Caddy so I don’t have much to go on.

1 Like

Thanks Francis! After you confirmed the CaddyFile was valid. I discovered an issue with my docker-compose. I had forgotten to tell the morty container where my .env file was that was supposed to make it listen on 0.0.0.0:3000.

1 Like

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