dockernginxself-hostinglaravel-forge

ERR_TOO_MANY_REDIRECTS when self-hosting Plausible behind NginX via Laravel Forge


I have a .app domain which I started hosting via Laravel Forge & Hetzner due to my main domain being a php-based app. Now, I want to host the self-hostable version of Plausible, plausible-ce so I've followed the guide seen in the link. I cloned the repo, updated the .env to include the secret, BASE_URL pointing to my sub.domain.app and HTTP_PORT=8000 HTTPS_PORT=443. I also added the compose.override.yml:

services:
  plausible:
    ports:
      - 8000:${HTTP_PORT}

I've seen some videos of people hosting it directly through Forge (which also does the certificate handling & nginx config), but I want to try to make it work directly on the server. I installed certbot and set-up the SSL certificates for the domain and all seemed to work fine. Furthermore, I created a plausible config for nginx in /etc/nginx/ named plausible, which contains:

server {
    server_name sub.domain.app;

    listen 80;
    listen [::]:80;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /live/websocket {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
}

As per plausible's documentation on reverse proxies I created a symbolic link in the sites-enabled directory and restarted nginx. Then I run docker compose up -d and the containers seem to be fine initially, but the page is still inaccessible.

Navigating to sub.domain.app leads to ERR_TOO_MANY_REDIRECTS and after taking a peek into the plausible-ce container's logs I see:

Loading plausible..                                                                                                                                                     
    Starting dependencies..
Starting repos..                                                                                                                                                                Plausible.Repo database already exists
Plausible.IngestRepo database already exists                                                                                                                                    Creation of Db successful!
Loading plausible..
Starting dependencies..
Starting repos..
15:47:40.197 [notice] Certificate for sub.domain.app is valid until 2025-07-12. Next renewal is scheduled for 2025-06-12.

So to me, it all looks fine so far, but when I try to reach the site, the docker logs this:

Request: GET /.well-known/acme-challenge/
** (exit) an exception was raised:
    ** (File.Error) could not read file "/var/lib/plausible/site_encrypt/certbot/acme-v02.api.letsencrypt.org/webroot/.well-known/acme-challenge": no such file or directory
        (elixir 1.18.3) lib/file.ex:385: File.read!/1
        (site_encrypt 0.6.0) lib/site_encrypt/acme_challenge.ex:12: SiteEncrypt.AcmeChallenge.call/2
        (plausible 0.0.1) lib/plausible_web/endpoint.ex:1: PlausibleWeb.Endpoint.plug_builder_call/2
        (plausible 0.0.1) lib/plausible_web/endpoint.ex:1: PlausibleWeb.Endpoint."call (overridable 3)"/2
        (plausible 0.0.1) lib/plausible_web/endpoint.ex:1: PlausibleWeb.Endpoint.call/2
        (plug_cowboy 2.7.3) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.13.0) /app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.13.0) /app/deps/cowboy/src/cowboy_stream_h.erl:310: :cowboy_stream_h.execute/3

Does this mean that the SSL certificate and the respective challenge aren't accessible from the container's volumes?

I also have this conf file in /etc/nginx/sites-enabled/plausible:

server {
    listen 80;
    listen [::]:80;
    server_name sub.domain.app;
location /.well-known/acme-challenge/ {
    root /var/lib/plausible/site_encrypt/certbot/acme-v02.api.letsencrypt.org/webroot/;
    try_files $uri =404;
}

    # Redirect all HTTP traffic to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl ipv6only=on;
    server_name sub.domain.app;

    # SSL configuration (handled by Certbot)
    ssl_certificate /etc/letsencrypt/live/sub.domain.app/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sub.domain.app/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Main proxy pass for Plausible
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Websocket support
    location /live/websocket {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;                                                                                                                                         proxy_set_header Connection "Upgrade";
    }                                                                                                                                                                           }

Could it maybe be an issue due to the network plausible containers use and the network that forge created?


Solution

  • ** (File.Error) could not read file "/var/lib/plausible/site_encrypt/certbot/..."
    

    That file path only makes sense if Plausible was in charge of the SSL challenge, but you're handling certs manually (correctly) through Nginx + Certbot, so your app is so confused it doesn't even know how to start.

    You're double-managing HTTPS , once via Nginx/Certbot, and again via Plausible's site_encrypt.

    Stop the second security mechanism. Edit your .env from plausible-ce and add:

    DISABLE_SSL=true