Originally created by @BlackrazorNZ on GitHub (May 24, 2025).
Hi,
While it works flawlessly on HTTPS certificates, I'm having a pig of a time getting Pangolin working with DNS challenge certificates. It got so frustrating that I destroyed and rebuilt my entire Pangolin VPS from scratch so that setting up DNS challenge for wildcard could be the first thing I did after completing the Pangolin install and setting up a Site - I've not even created any Resources yet.
The DNS challenge attempt via Cloudfare seems to work fine (and I can verify seperately that the Cloudflare DNS API call is working correctly as it generates a DNS challenge cert almost instantly in NGINX Proxy Manager), but any attempt to access the Pangolin frontend is met with a 404 error.
Given that I've followed the instructions in the Pangolin Docs to the letter, I'm stumped as to where to go here. I have changed nothing post-install apart from the exact changes recommended in the documentation for wildcard certs. If I revert the changes (via a backup of the config directory etc) it starts working again immediately, albeit via HTTPS certificate.
Any advice on what could be wrong? I've spent 5 hours on this now just trying to debug the issue. Relevant (redacted) files below.
docker-compose.yml :
name: pangolin
services:
pangolin:
image: fosrl/pangolin:1.4.0
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: fosrl/gerbil:1.0.0
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3003
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 443:443 # Port for traefik because of the network_mode
- 80:80 # Port for traefik because of the network_mode
traefik:
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
# Add the environment variables for your DNS provider.
environment:
CLOUDFLARE_DNS_API_TOKEN: "[MY-CLOUDFLARE-TOKEN-HERE]"
volumes:
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
networks:
default:
driver: bridge
name: pangolin
Originally created by @BlackrazorNZ on GitHub (May 24, 2025).
Hi,
While it works flawlessly on HTTPS certificates, I'm having a pig of a time getting Pangolin working with DNS challenge certificates. It got so frustrating that I destroyed and rebuilt my entire Pangolin VPS from scratch so that setting up DNS challenge for wildcard could be the first thing I did after completing the Pangolin install and setting up a Site - I've not even created any Resources yet.
The DNS challenge attempt via Cloudfare seems to work fine (and I can verify seperately that the Cloudflare DNS API call is working correctly as it generates a DNS challenge cert almost instantly in NGINX Proxy Manager), but any attempt to access the Pangolin frontend is met with a 404 error.
Given that I've followed the instructions in the Pangolin Docs to the letter, I'm stumped as to where to go here. I have changed nothing post-install apart from the exact changes recommended in the documentation for wildcard certs. If I revert the changes (via a backup of the config directory etc) it starts working again immediately, albeit via HTTPS certificate.
Any advice on what could be wrong? I've spent 5 hours on this now just trying to debug the issue. Relevant (redacted) files below.
docker-compose.yml :
```
name: pangolin
services:
pangolin:
image: fosrl/pangolin:1.4.0
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: fosrl/gerbil:1.0.0
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3003
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 443:443 # Port for traefik because of the network_mode
- 80:80 # Port for traefik because of the network_mode
traefik:
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
# Add the environment variables for your DNS provider.
environment:
CLOUDFLARE_DNS_API_TOKEN: "[MY-CLOUDFLARE-TOKEN-HERE]"
volumes:
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
networks:
default:
driver: bridge
name: pangolin
```
traefik_config.yml (from config/traefik):
```
api:
insecure: true
dashboard: true
providers:
http:
endpoint: "http://pangolin:3001/api/v1/traefik-config"
pollInterval: "5s"
file:
filename: "/etc/traefik/dynamic_config.yml"
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.1.0"
log:
level: "DEBUG"
format: "common"
certificatesResolvers:
letsencrypt:
acme:
# httpChallenge:
# entryPoint: web
dnsChallenge:
provider: "cloudflare"
email: "it@[MYDOMAIN]"
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: "30m"
http:
tls:
certResolver: "letsencrypt"
serversTransport:
insecureSkipVerify: true
```
dynamic_config.yml (from config/traefik):
```
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
routers:
# HTTP to HTTPS redirect router
main-app-router-redirect:
rule: "Host(`pangolin.[MYDOMAIN]`)"
service: next-service
entryPoints:
- web
middlewares:
- redirect-to-https
# Next.js router (handles everything except API and WebSocket paths)
next-router:
rule: "Host(`pangolin.[MYDOMAIN]`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
domains:
- main: "[MYDOMAIN]"
sans:
- "*.[MYDOMAIN]"
# API router (handles /api/v1 paths)
api-router:
rule: "Host(`pangolin.[MYDOMAIN]`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
# WebSocket router
ws-router:
rule: "Host(`pangolin.[MYDOMAIN]`)"
service: api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002" # Next.js server
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000" # API/WebSocket server
```
config.yml (from config):
```
# To see all available options, please visit the docs:
# https://docs.fossorial.io/Pangolin/Configuration/config
app:
dashboard_url: "https://pangolin.[MYDOMAIN]"
log_level: "info"
save_logs: false
domains:
domain1:
base_domain: "[MYDOMAIN]"
cert_resolver: "letsencrypt"
server:
external_port: 3000
internal_port: 3001
next_port: 3002
internal_hostname: "pangolin"
session_cookie_name: "p_session_token"
resource_access_token_param: "p_token"
resource_access_token_headers:
id: "P-Access-Token-Id"
token: "P-Access-Token"
resource_session_request_param: "p_session_request"
secret: [MYSECRET]
cors:
origins: ["https://pangolin.[MYDOMAIN]"]
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
headers: ["X-CSRF-Token", "Content-Type"]
credentials: false
traefik:
cert_resolver: "letsencrypt"
http_entrypoint: "web"
https_entrypoint: "websecure"
gerbil:
start_port: 51820
base_endpoint: "pangolin.[MYDOMAIN]"
use_subdomain: false
block_size: 24
site_block_size: 30
subnet_group: 100.89.137.0/20
rate_limits:
global:
window_minutes: 1
max_requests: 500
users:
server_admin:
email: "it@[MYDOMAIN]"
password: "[MYPASSWORD]"
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: true
allow_raw_resources: true
allow_base_domain_resources: true
prefer_wildcard_cert: true #Added per instruction on the Pangolin Docs
```
acme.json (from config/letsencrypt):
```
{
"letsencrypt": {
"Account": {
"Email": "it@[MYDOMAIN]",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:it@[MYDOMAIN]"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/2417999267"
},
"PrivateKey": "[PRIVATE-KEY]",
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "pangolin.[MYDOMAIN]"
},
"certificate": "[CERTIFICATE]",
"key": "[KEY]]",
"Store": "default"
}
]
}
}
```
Hey there, not a pangolin expert but I just spent a few hours last night on this (though my issue was caused by something else)
I didn't notice any difference in using the prefer_wildcard_certs flag in my testing, nor did I need to specify the domains/sans field (though I did this one anyways).
What stood out to me in the output you shared is this line:
traefik | 2025-05-24T01:36:14Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default
Your config looks almost identical to my working wildcard + Pangolin setup. For troubleshooting, I would try the following:
Edit your config/traefik/traefik_config.yml file to increase the verbosity of logging and follow the logs using docker compose logs -f in a second terminal window. Under the log section, change level: INFO to level: DEBUG.
Delete your old certs (from config/letsencrypt) by deleting that acme.json file.
Add the staging letsencrypt URL (caServer: https://acme-staging-v02.api.letsencrypt.org/directory) so you don't inadvertently hit the rate limit while troubleshooting this. If it works, you should have your acme.json file populated again (mine was about 40k file size from about 6 domains) and if you go to a website, it'll show a cert error, and the cert will show Lets Encrypt Staging.
I think because you already have certs, net-new ones aren't being requested. I got this from the 'Verify it works' section of the doc page on this topic
Good luck!
@kmanwar89 commented on GitHub (May 26, 2025):
Hey there, not a pangolin expert but I just spent a few hours last night on this (though my issue was caused by something else)
I didn't notice any difference in using the `prefer_wildcard_certs` flag in my testing, nor did I need to specify the domains/sans field (though I did this one anyways).
What stood out to me in the output you shared is this line:
`traefik | 2025-05-24T01:36:14Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=default`
Your config looks almost identical to my working wildcard + Pangolin setup. For troubleshooting, I would try the following:
1. Edit your `config/traefik/traefik_config.yml` file to increase the verbosity of logging and follow the logs using `docker compose logs -f` in a second terminal window. Under the `log` section, change `level: INFO` to `level: DEBUG`.
2. Delete your old certs (from `config/letsencrypt`) by deleting that `acme.json` file.
3. Add the `staging` letsencrypt URL (`caServer: https://acme-staging-v02.api.letsencrypt.org/directory`) so you don't inadvertently hit the rate limit while troubleshooting this. If it works, you should have your `acme.json` file populated again (mine was about 40k file size from about 6 domains) and if you go to a website, it'll show a cert error, and the cert will show Lets Encrypt Staging.
I think because you already have certs, net-new ones aren't being requested. I got this from the 'Verify it works' section of the [doc page on this topic](https://docs.fossorial.io/Pangolin/Configuration/wildcard-certs#verify-it-works)
Good luck!
Closing this one, figured it out with assistance from the Discord group. The issue is that the 'domains' value in dynamic_config.yml was incorrectly indented, and needs to be formatted so that the 'domains' is indented to align with 'certResolver' like so:
@BlackrazorNZ commented on GitHub (May 27, 2025):
Closing this one, figured it out with assistance from the Discord group. The issue is that the 'domains' value in `dynamic_config.yml` was incorrectly indented, and needs to be formatted so that the 'domains' is indented to align with 'certResolver' like so:
certResolver: letsencrypt
domains:
- main: "[MYDOMAIN]"
sans: "*.[MYDOMAIN]"
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Originally created by @BlackrazorNZ on GitHub (May 24, 2025).
Hi,
While it works flawlessly on HTTPS certificates, I'm having a pig of a time getting Pangolin working with DNS challenge certificates. It got so frustrating that I destroyed and rebuilt my entire Pangolin VPS from scratch so that setting up DNS challenge for wildcard could be the first thing I did after completing the Pangolin install and setting up a Site - I've not even created any Resources yet.
The DNS challenge attempt via Cloudfare seems to work fine (and I can verify seperately that the Cloudflare DNS API call is working correctly as it generates a DNS challenge cert almost instantly in NGINX Proxy Manager), but any attempt to access the Pangolin frontend is met with a 404 error.
Given that I've followed the instructions in the Pangolin Docs to the letter, I'm stumped as to where to go here. I have changed nothing post-install apart from the exact changes recommended in the documentation for wildcard certs. If I revert the changes (via a backup of the config directory etc) it starts working again immediately, albeit via HTTPS certificate.
Any advice on what could be wrong? I've spent 5 hours on this now just trying to debug the issue. Relevant (redacted) files below.
docker-compose.yml :
traefik_config.yml (from config/traefik):
dynamic_config.yml (from config/traefik):
config.yml (from config):
acme.json (from config/letsencrypt):
@BlackrazorNZ commented on GitHub (May 24, 2025):
A log dump from 'docker compose logs -f' follows, approximately 2 minutes after bringing the docker stack up:
@kmanwar89 commented on GitHub (May 26, 2025):
Hey there, not a pangolin expert but I just spent a few hours last night on this (though my issue was caused by something else)
I didn't notice any difference in using the
prefer_wildcard_certsflag in my testing, nor did I need to specify the domains/sans field (though I did this one anyways).What stood out to me in the output you shared is this line:
traefik | 2025-05-24T01:36:14Z DBG github.com/traefik/traefik/v3/pkg/tls/tlsmanager.go:321 > No default certificate, fallback to the internal generated certificate tlsStoreName=defaultYour config looks almost identical to my working wildcard + Pangolin setup. For troubleshooting, I would try the following:
config/traefik/traefik_config.ymlfile to increase the verbosity of logging and follow the logs usingdocker compose logs -fin a second terminal window. Under thelogsection, changelevel: INFOtolevel: DEBUG.config/letsencrypt) by deleting thatacme.jsonfile.stagingletsencrypt URL (caServer: https://acme-staging-v02.api.letsencrypt.org/directory) so you don't inadvertently hit the rate limit while troubleshooting this. If it works, you should have youracme.jsonfile populated again (mine was about 40k file size from about 6 domains) and if you go to a website, it'll show a cert error, and the cert will show Lets Encrypt Staging.I think because you already have certs, net-new ones aren't being requested. I got this from the 'Verify it works' section of the doc page on this topic
Good luck!
@BlackrazorNZ commented on GitHub (May 27, 2025):
Closing this one, figured it out with assistance from the Discord group. The issue is that the 'domains' value in
dynamic_config.ymlwas incorrectly indented, and needs to be formatted so that the 'domains' is indented to align with 'certResolver' like so: