How to Self-Host Plausible Analytics With Docker Compose
Plausible Community Edition (CE) is the self-hosted version of Plausible, a web analytics service. Docker Compose uses a configuration file to run…
When you run a reverse proxy on the same host as a rootless Podman container, publish the container’s service on the host’s loopback address, which the host uses to reach itself, and point the proxy there. A reverse proxy accepts client requests and forwards them to a backend service. Rootless Podman runs containers without giving Podman root privileges on the host.
For example, configure the application inside the container to listen on 0.0.0.0:8080, publish its port as 127.0.0.1:18080 on the host, and have the host’s NGINX proxy connect to http://127.0.0.1:18080. These addresses serve different purposes: the application listens on an address inside the container, while Podman makes the published port available only through the host’s loopback interface.
Run the container with an explicit host address:
podman run -d --name webapp \
-p 127.0.0.1:18080:8080 \
IMAGE
Replace IMAGE with the image you want to run. In the port mapping, 18080 is the host port and 8080 is the container port. Configure the application in the image to listen on 0.0.0.0:8080, so it accepts connections on the container’s network interfaces.
The 127.0.0.1 in the mapping makes the published port available only at the host’s IPv4 loopback address, which lets the host connect to itself. Podman documents that omitting the host address binds the port on all host addresses, so a mapping such as -p 18080:8080 does not express the same local-only intent. Check the mapping with podman port, and configure IPv6 loopback separately if your proxy uses it.
For NGINX running directly on the host, set the upstream to the host’s loopback address:
server {
listen 80;
server_name example.test;
location / {
proxy_pass http://127.0.0.1:18080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
NGINX uses proxy_pass to forward requests to the upstream address. The headers pass the requested host name and client-address information to the backend. Treat forwarded client-address headers as proxy metadata: if another proxy sits in front of NGINX, configure your trusted-proxy handling for that actual network path.
The host and container each have a network namespace, which determines the network interfaces and addresses a process can use. Thus, 127.0.0.1 in host NGINX refers to the host, while 127.0.0.1 inside the application container refers to that container’s loopback interface.
Bind the application to 0.0.0.0 inside the container, even though Podman publishes the host port only on 127.0.0.1. If the application listens only on container loopback, Podman’s port-forwarding path may not reach it. Podman’s troubleshooting guidance documents connection resets in this situation and recommends listening on 0.0.0.0 inside the container.
Rootless networking behavior depends on the Podman version and networking backend. A report about Podman 5.2 with pasta describes a different result for container-loopback listeners, but the general setup should use the documented 0.0.0.0 binding rather than depend on backend-specific behavior.
The host-proxy configuration does not work unchanged when NGINX runs in a separate container. There, 127.0.0.1:18080 points to the NGINX container itself, not the host.
If NGINX and the application both run in containers, connect them through a shared Podman network. Then configure NGINX to reach the application by its network name and container port. Alternatively, put both containers in one Podman pod. Containers in a pod share a network namespace, so they can communicate with each other over localhost. Publish the proxy’s external-facing port from the pod. Podman describes these pod networking behaviors.
Host networking is another option, but it gives a container access to host-local network sockets and carries broader security implications. Use it only after reviewing what host services the container could reach. Podman’s network documentation describes rootless networking modes and their configuration.
First confirm that Podman published the intended address and port:
podman port webapp 8080/tcp
Then test the backend from the host:
curl -i http://127.0.0.1:18080/
If that request fails or resets, check that the application listens on the container port and on 0.0.0.0, rather than only on container loopback. If the host-side request succeeds but a request through NGINX fails, check NGINX’s upstream address and virtual-host configuration.
A rootless container also fails to publish directly on a privileged port such as 80 when the host restricts unprivileged processes from binding below port 1024. A host-level proxy can bind the public port and forward traffic to the higher loopback port, as in the example above.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com