The last post put a private door on the network, so I can reach anything on the LAN from anywhere. But reaching a host and managing it are two different things. If you've got more than one machine running Docker, and most homelabs drift that way fast, a NAS here, a mini PC there, a spare Linux laptop pressed into service, you end up bouncing between SSH sessions and separate browser tabs just to see what's running where.
Portainer fixes that with a server-and-agent model: one interface, every host. This post wires a second Docker host into a single Portainer and, more importantly, does it without turning the management layer into a new thing exposed to the internet.
Server and agents
Portainer has two roles. The Portainer Server is the brain and the web UI, and you run exactly one of them. An Agent is a small container that runs on each additional host you want to manage. The server talks to each agent, and every host's containers, images, volumes and stacks show up together under one pane, with a dropdown to switch between them.
The point is that you don't run a full Portainer on every box. One server, one lightweight agent per extra host, and the whole fleet is visible and manageable from a single login.
The part most guides skip
An agent isn't a read-only viewer. It mounts the host's Docker socket, which means whoever controls the Portainer Server effectively has root over Docker on every agent host: start and stop anything, mount any volume, run a new container as privileged. That's a lot of power concentrated in one login.
So the agent stays on the LAN, full stop. It never gets a port forward, never a public subdomain, never a spot behind the reverse proxy. When I'm away and need to manage something, I go in through the WireGuard door from the last post and reach Portainer at its normal internal address, exactly as if I were home. It's the same question the whole series keeps asking, now pointed at the control plane itself: does this need to be reachable by everyone? No. So it isn't.
Prerequisites
- A running Portainer Server. If you followed the earlier deployment pattern, you already have one; if not, it's a single container stack.
- A second Docker host on the same LAN as the server.
Step 1: Deploy the agent on the second host
On the host you want to add, deploy the agent as a small stack:
services:
portainer_agent:
image: portainer/agent:lts
container_name: portainer_agent
restart: always
ports:
- 9001:9001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/docker/volumes:/var/lib/docker/volumes
Port 9001 is the agent's standard port, and it only ever needs to be reachable from your Portainer Server across the LAN. Do not forward it at your router. The docker socket mount is what gives the agent its reach over that host, which is exactly why the previous section matters.
Step 2: Add the environment
In the Portainer Server UI, go to Environments, then Add environment, and start the wizard. Choose Docker Standalone, then Agent. For the environment URL, enter the agent host and its port, like 192.168.1.50:9001, with no tcp:// or https:// in front. The server and agent negotiate an encrypted connection on their own. Click Connect.
One quirk worth knowing: by default an agent pairs with the first Portainer Server that connects to it and refuses any other server afterward. That's a deliberate safety measure. If you genuinely need more than one server to reach the same agent, that's what the shared secret in the next box is for.
Once it's connected, the new host appears in your environment list alongside the first.
For advanced users: a shared secret, and hosts that aren't on your LAN
If you set an
AGENT_SECRETon the Portainer Server, pass the same value to the agent (an environment variable at deploy time) so the two only pair when the secret matches. It's cheap extra assurance about which server owns which agent.For a host that isn't on your LAN, a VPS or a remote site, don't open
9001to the internet. Use the Edge Agent instead. It dials out to the server rather than waiting to be connected to, so nothing inbound needs opening and it works fine behind NAT or CGNAT. It's the same outbound pattern the monitoring and security agents in earlier posts used, and Portainer now recommends it as the default for new remote environments.
Step 3: Use it
Back in the UI, the environment switcher lets you jump between hosts. Deploy a stack to the NAS or to the laptop by picking the target environment first, browse either host's volumes, check logs, restart a container, all from one place. The mental overhead of "which machine was that running on again" mostly disappears, because you can just look.
Convenience with a blast radius
It's worth being honest about the trade you're making. Consolidating every host under one login is a real quality-of-life win, and it's also a single point of failure: whoever gets into that Portainer Server can touch every host paired with it. That's not a reason to avoid it, it's a reason to treat the server as high-value. Keep it internal, keep it patched, use a strong admin password, and turn on multi-factor auth. The agent hardening above only matters if the server in front of it is locked down too.
The spine, assembled
Five posts in, the shape of the whole thing is visible. There's one hardened public door for the things meant to be found, the reverse proxy with TLS and CrowdSec watching it. There's one key-only private door for everything else, WireGuard on the gateway. And behind that private door, a single control plane over the entire fleet, deliberately kept off the internet and reached only from inside.
That's the homelab spine: a clear rule for what faces outward, a clean way back in for what doesn't, and one pane of glass over all of it. Everything after this is just deciding what to run on top.
Managing a fleet a different way, or wrestling with Edge Agents behind CGNAT? Reach out to me and I'll fold the good cases into a later post.