What it is
Portainer is a web UI on top of the Docker socket. From a single browser tab I can see every container across every Docker LXC, stream logs, exec into a shell, restart a service, and edit a stack's compose file. It's a single Portainer instance with agents installed on each remote Docker LXC.
Why I run it
I have five Docker LXCs. Managing them by SSHing into each one and running docker compose commands gets old after the second one. Portainer collapses the five hosts into one view — I can see which containers are running where, how they're using resources, and what their logs say without leaving the browser.
I deliberately don't use Portainer to deploy most stacks, though. Host-managed compose files in version control are easier to back up, easier to edit in the editor of my choice, and don't tie me to Portainer continuing to exist. Portainer is the viewer and operator interface, not the source of truth.
How I use it
The main Portainer instance runs on the edge LXC. Each remote Docker LXC runs a Portainer Agent on port 9001 that exposes the local Docker socket back to Portainer over a TLS-authenticated TCP connection. The main UI shows them all in one Environments list — the local socket as one, each remote agent as another — and I switch between them with a dropdown.
The daily uses:
- Tail container logs when something looks off. Faster than SSH for a quick read.
- Exec into a container to poke at state — handy for SQLite-backed services where I want to query the DB directly.
- Restart a misbehaving container without remembering which LXC it lives on.
- Sanity-check resource usage in the per-container CPU/RAM graphs, especially before a planned change.
Stack editing and deployment happens via host-managed compose files on disk, not Portainer's stack UI. Portainer can manage them too — that's a Portainer stack — but a host-managed compose file is just a YAML file under /opt/stacks/<service>/ that I version-control and edit with nano.
Setup notes
- Host: edge LXC, alongside NPM and AdGuard. Runs as a standalone container — not in any compose stack — because Portainer can't reliably manage its own upgrade if it goes down.
- Reverse proxy: behind NPM with Force SSL on and websockets enabled (websockets are required for the container console and live logs to work). The Portainer container itself is started with
--trusted-origins=<my-portainer-hostname>so requests through the reverse proxy pass Portainer's CSRF origin check. Without that flag, every state-changing operation through the proxied hostname returns 403 "origin invalid." - Backups: the
portainer_datanamed volume holds environments, users, settings. Captured by PBS via the LXC's rootfs. - Update cadence: manual. Image pinned. Portainer's "Update available" banner is informational, not actionable on its own.
Runbook
- Healthy looks like: every environment in the Environments list shows status
Upwith correct container counts. --trusted-originsflag is hostname-only: no scheme, no port.portainer.labworks;https://portainer.laborportainer.lab:443does not. Also, the flag is a Portainer application argument, not a Dockerrunflag — it goes after the image name in thedocker runcommand, not before.- An agent shows "Down": most often the LXC is off, the agent container crashed, or there's a network problem. From the main host,
nc -zv <agent-IP> 9001confirms reachability. - 403 origin invalid on edits via the proxied hostname: the Portainer container is running without
--trusted-origins. Recreate with the flag. - Adding an environment fails with "Unable to connect": usually a scheme prefix slipped into the address field. The address is
host:port, nohttps://. - Where logs live:
docker logs portaineron the host for the UI itself, the per-container log streams in the UI for everything else.