The Podman remote API lets you manage containers, pods, and images on a remote machine as if they were local, all through a simple REST interface.

Let’s see it in action. Imagine you have a server running Podman, and you want to list its containers from your local laptop.

First, on the remote server, you need to enable and start the Podman API service. This is typically done by creating a systemd service file.

# /etc/systemd/system/podman-api.service
[Unit]
Description=Podman API Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/podman system service --time=0
Restart=on-failure
# If you need to bind to a specific IP other than localhost:
# ExecStart=/usr/bin/podman system service --time=0 -H tcp://192.168.1.100:8080

[Install]
WantedBy=multi-user.target

Then, enable and start it:

sudo systemctl enable podman-api.service
sudo systemctl start podman-api.service

The --time=0 flag keeps the service running indefinitely. Without it, the API would stop after a period of inactivity.

Now, from your local machine, you can use curl to interact with this API. The default endpoint is unix:///run/podman/podman.sock if you’re running Podman locally, but for remote access, you’ll use a TCP endpoint. Let’s assume the remote server’s IP is 192.168.1.100 and it’s listening on port 8080.

To list containers on the remote server:

curl --unix-socket /run/podman/podman.sock http://localhost/v3.0.0/containers/json

If you configured the remote service to listen on a TCP port, say 8080, you’d use:

curl http://192.168.1.100:8080/v3.0.0/containers/json

This returns a JSON array of all containers running on the remote machine, much like podman ps would locally.

You can also create a container remotely:

curl -X POST \
  --unix-socket /run/podman/podman.sock \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "docker.io/library/alpine:latest",
    "Cmd": ["echo", "hello from remote podman"],
    "HostConfig": {
      "AutoRemove": true
    }
  }' \
  http://localhost/v3.0.0/containers/create

This command sends a POST request to the /containers/create endpoint with the container configuration in JSON format. It instructs Podman to pull the alpine:latest image and create a container that runs echo "hello from remote podman". The AutoRemove: true in HostConfig means the container will be automatically removed once it exits.

The response will include the ID of the newly created container. You can then use this ID to start it:

CONTAINER_ID=$(curl --unix-socket /run/podman/podman.sock http://localhost/v3.0.0/containers/create \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"Image": "docker.io/library/alpine:latest", "Cmd": ["echo", "hello from remote podman"], "HostConfig": {"AutoRemove": true}}' | jq -r '.Id')

curl --unix-socket /run/podman/podman.sock http://localhost/v3.0.0/containers/$CONTAINER_ID/start -X POST

The jq -r '.Id' part extracts just the container ID from the JSON output of the create command.

The Podman remote API exposes most of the functionality available through the podman CLI. This includes managing images (pull, push, build), volumes, networks, and even running Podman Play (using podman-compose or Kubernetes YAMLs). It’s a powerful way to automate container operations across multiple machines or to integrate container management into larger applications and workflows.

The API version in the URL, like /v3.0.0/, is important. It ensures backward compatibility. If you upgrade Podman on the server, the API might change, but older clients can still connect using the version they expect, as long as that version is still supported. Podman keeps multiple API versions available for a period.

The Podman system service can be configured to listen on a specific TCP address and port using the -H flag, like podman system service -H tcp://0.0.0.0:8080 --time=0. This allows remote clients to connect without needing SSH tunneling. However, this exposes the Podman daemon directly to the network, so securing this endpoint (e.g., with a firewall or TLS) is crucial for production environments.

When you use podman system service, it creates a Unix domain socket by default, typically at /run/podman/podman.sock. This socket is what local podman commands use to communicate with the Podman daemon. When you specify tcp://host:port with the -H flag, you’re telling the Podman daemon to listen for HTTP requests on that network address and port, making it accessible from other machines.

You can also start the Podman system service interactively without a systemd unit:

podman system service --time=0

This will print a message indicating the service is listening, usually on the Unix socket. To expose it over TCP, use:

podman system service --time=0 -H tcp://0.0.0.0:8080

This command binds the service to all available network interfaces on port 8080. It’s vital to understand that this makes your Podman daemon accessible from any machine that can reach 192.168.1.100:8080. For security, you’d typically restrict access via firewall rules or use TLS encryption if the API endpoint supports it (which it does via the --tls-cert-dir option).

The most surprising thing about the Podman remote API is how seamlessly it mirrors the CLI functionality. You can execute nearly any podman command by translating it into an HTTP request to the appropriate API endpoint. This isn’t just for basic container operations; it extends to building images with dockerfile or containerfile, managing complex network setups, and even running Kubernetes YAMLs directly through the API, effectively turning Podman into a remote container orchestrator.

The next concept you’ll likely encounter is managing authentication and authorization for the remote API, especially when exposing it over TCP.

Want structured learning?

Take the full Podman course →