Controlling Podman from a distance: opening the REST API with podman system service, Docker API and docker-py compatibility, securing the connection with SSH, and orchestrating containers from another host using podman-remote.

In episode 14 you secured images and credentials with secrets, signatures, and Trivy. Now imagine a cluster with dozens of hosts — typing podman run on every machine isn't a scalable way to work. Episode 15 opens two paths for remote control: the REST API, compatible with Docker, and podman-remote, a native client that communicates over SSH.
Podman is indeed daemonless, but this component opens a controlled exception: podman system service runs an API service that is entirely optional. Without this service, Podman keeps working normally. With it, the machine becomes a server that can receive requests from outside — both Docker-compatible clients and podman-remote.
podman system service --time=0
podman system service --time=5 unix:///tmp/podman.sockThe last argument is the endpoint. A unix socket is used by default, and --time=0 disables the timeout. For production use, run this service as a systemd unit so it restarts automatically if it dies.
This service serves two families of APIs at once:
libpod prefix, for example pod, machine, and healthcheck operations.Both families can be called directly over HTTP:
curl --unix-socket /run/podman/podman.sock http://localhost/_ping
curl --unix-socket /run/podman/podman.sock http://localhost/containers/jsonThe existence of a Docker-compatible API means the Docker client ecosystem works too. docker-py is the most common example:
from docker import DockerClient
client = DockerClient(base_url="unix:///run/podman/podman.sock")
containers = client.containers.list()
client.containers.run("nginx:alpine", ports={"80/tcp": 8080})Note the only change: base_url points to the Podman socket. Orchestration scripts you've already written for Docker often need just one line changed. The same applies to docker compose pointed at it via DOCKER_HOST.
A unix socket is secure because it's limited by file access. Once the API is opened over TCP, you must add protection: run it behind an SSH tunnel, restrict it to a private network, or use TLS. Never expose the API to the open internet without protection.
The simplest way to secure an API opened on TCP is not to open it to the public at all. Leave podman system service listening on localhost, then create an SSH tunnel to the remote machine:
ssh -L 8080:127.0.0.1:8080 deploy@10.0.0.5
export DOCKER_HOST=tcp://127.0.0.1:8080
docker psWith this pattern, traffic is encrypted end-to-end, authentication is handled by SSH, and port 8080 on the remote host is never visible to other networks. This approach can also be used with podman-remote, although in practice podman-remote already uses SSH directly.
Tip
Don't mix directions: the SSH tunnel points at the API socket, while podman-remote builds its own connection to the target socket via SSH. Pick one pattern per workflow so debugging doesn't get confusing.
For daily use, the more convenient client is podman-remote — the same binary as Podman but focused on communicating with a server:
podman system connection add prod --identity ~/.ssh/id_ed25519 ssh://deploy@10.0.0.5/run/podman/podman.sock
podman-remote -c prod ps
podman-remote -c prod run -d --name web nginx:alpinepodman system connection add stores the connection configuration. After that, podman-remote commands, identical to local podman, run on the remote machine.
The podman-remote connection runs over SSH, so all SSH security applies: key authentication, forwarding to the target socket, and traffic encryption. Recommended practices:
prod, staging) in containers.conf.| Aspect | REST API | podman-remote |
|---|---|---|
| Protocol | HTTP / Unix socket | SSH |
| Authentication | Socket permission, TLS, tunnel | SSH key |
| Clients | docker CLI, docker-py, curl | The podman-remote binary |
| Best for | Tooling and pipeline integration | Daily admin from another host |
| Compatibility | Docker-compatible | Native Podman subcommands |
Choose based on need: API for automation and integration, remote client for humans who want the full CLI.
Important
Watch out for identity confusion: podman system service and podman-remote can both feel the same but work differently. Don't run podman system service permanently without a reason — daemonless remains Podman's main feature, and every open socket adds attack surface.
In episode 15 you controlled Podman from a distance: opening the REST API with podman system service, getting to know the Libpod and Docker-compatible endpoints, using docker-py via the socket, securing the connection with SSH, and orchestrating containers from another host using podman-remote.
The key points to take home:
base_url or DOCKER_HOST.In the next episode, Episode 16, we'll harden what's already in place: isolation & hardening — limiting CPU, memory, and PIDs with cgroups v2, read-only rootfs, and network isolation and auditing.