Podman Socket and Docker API Compatibility

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package podman-5.8.2-5.el10_2.x86_64
podman-docker-5.8.2-5.el10_2.x86_64
Applies to Linux hosts where Docker-compatible tools must talk to Podman through a Unix socket or DOCKER_HOST
Privilege Rootless user socket for daily use; rootful socket and podman-docker symlink need administrator access
Scope podman.socket and socket activation, rootless versus rootful socket paths, podman system service, Docker-compatible and Libpod REST endpoints, cautious API version behavior, DOCKER_HOST, docker compose against the socket, RHEL podman-docker and /var/run/docker.sock, external client testing, Swarm API gap, socket security, brief mTLS and in-container socket mounts, boot enablement with linger, and troubleshooting. Does not cover Compose provider selection, full Docker-to-Podman migration, or Podman remote SSH workflows in depth.
Related guides Podman vs Docker
Podman architecture
Install Podman on Rocky Linux

Normal podman run does not need a background daemon. That is different from software written for Docker Engine, which often opens a Unix socket and speaks the Docker REST API.

Podman reconciles both models: CLI commands stay fork-exec, and an optional podman.socket unit starts podman system service when a client connects. This guide shows which socket path controls which container store, how to test the API, and what Docker-compatible tools can realistically expect.


What is podman.socket?

text
Docker-compatible client
Unix socket (podman.sock)
systemd socket activation
podman system service
Podman engine

The socket can listen idle until a client connects, then systemd launches the API service. That is how Podman stays daemonless for CLI use while still offering a Docker-compatible REST API for Compose, CI runners, and language SDKs.

You only need the socket when a tool cannot call podman directly — for example when it hard-codes unix:///var/run/docker.sock, reads DOCKER_HOST, or uses a Docker SDK. Day-to-day podman run and podman ps never touch it.


Two REST API layers on one socket

Podman exposes two interfaces on the same Unix socket:

Layer Purpose
Docker-compatible API Endpoints Docker clients expect — /v1.40/..., _ping, container and image routes
Libpod API Podman-native routes under /v4.0.0/libpod/... with features Docker API does not cover

Do not assume every Libpod capability has a Docker API equivalent. Tools that only speak Docker API stay on the compatibility routes.


Docker API version compatibility

Podman documents its compatibility layer against the Docker v1.40 API. The server may accept requests carrying other version prefixes, so the reported ApiVersion does not guarantee complete dockerd feature parity. On Podman 5.8.2, the version JSON can still report broader numbers:

json
"ApiVersion": "1.44",
"MinAPIVersion": "1.24"

Test the endpoints your application actually calls — feature support matters more than the version header alone.


Start the rootless Podman socket

Run these commands as a normal login user — not as root. Enable the user socket:

bash
systemctl --user start podman.socket

Check that systemd is listening:

bash
systemctl --user status podman.socket

Sample output (tested as user podtest on the lab host):

output
● podman.socket - Podman API Socket
     Active: active (listening)
     Listen: /run/user/1014/podman/podman.sock (Stream)
   Triggers: ● podman.service

Typical path:

text
/run/user/$UID/podman/podman.sock

For example, UID 1000 uses /run/user/1000/podman/podman.sock. The same variable form is:

text
$XDG_RUNTIME_DIR/podman/podman.sock

Confirm the file exists and is owned by your user:

bash
ls -l "$XDG_RUNTIME_DIR/podman/podman.sock"
output
srw-rw----. 1 podtest podtest 0 Aug 23 18:25 /run/user/1014/podman/podman.sock

Rootless socket operations use that user's Podman image and container store. If you see /run/user/0/... and owner root, you started the user socket as root — that is not a rootless workflow.


Start the rootful Podman socket

System-wide socket for root's Podman store:

bash
sudo systemctl start podman.socket

Confirm the rootful unit is listening on /run/podman/podman.sock:

bash
sudo systemctl status podman.socket

Sample output:

output
● podman.socket - Podman API Socket
     Active: active (listening)
     Listen: /run/podman/podman.sock (Stream)

Path:

text
/run/podman/podman.sock
Socket Podman context
$XDG_RUNTIME_DIR/podman/podman.sock Rootless — containers and images for that user
/run/podman/podman.sock Rootful — root's container store

A client pointed at the wrong socket sees the wrong containers. Always match DOCKER_HOST to the user context you intend.


Socket activation and podman.service

Socket activation means the API process starts only when a client connects. Check the service unit before any client has connected:

bash
systemctl --user status podman.service

Sample output while idle:

output
○ podman.service - Podman API Service
     Active: inactive (dead)
TriggeredBy: ● podman.socket

That inactive state is normal. Hit the socket with any API client — curl works — and systemd starts the service:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/_ping

Re-check the service unit:

bash
systemctl --user status podman.service

Sample output after activation:

output
● podman.service - Podman API Service
     Active: active (running)
   Main PID: 304547 (podman)
             └─304547 /usr/bin/podman --log-level=info system service

When idle timeout expires, the service process can exit while podman.socket keeps listening. An inactive podman.service is not a failure if the socket unit is active.


Run podman system service manually

For debugging, run the API service in the foreground:

bash
podman system service --time 0

--time 0 disables inactivity shutdown. You can bind a custom socket:

bash
podman system service --time 0 unix:///tmp/podman-test.sock

Use systemd socket activation for normal operation — not a manual foreground process left running forever.


Test the API with curl

Docker-compatible health check:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/_ping
output
OK

Query Docker-compatible version info:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/v1.40/version

Sample output (trimmed):

output
{"Platform":{"Name":"Red Hat Enterprise Linux 10.2 (Coughlan)"},"Components":[{"Name":"Podman Engine","Version":"5.8.2",...}],"Version":"28.3.3","ApiVersion":"1.44","MinAPIVersion":"1.24",...}

The ApiVersion field is what Docker clients read; on this host it reports 1.44 even when you request /v1.40/version. That is why feature testing beats trusting the version string alone.

Libpod ping uses a separate route:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/libpod/_ping
output
OK

Libpod info exposes Podman-native host details:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/v4.0.0/libpod/info

Parse version.Version and host.security.rootless when you need to confirm which store the socket serves.


Configure DOCKER_HOST

DOCKER_HOST is for clients that speak the Docker REST API over a socket — language SDKs, Testcontainers, and other tools that open unix://... and call HTTP routes. It is separate from the podman-docker package, which provides a docker command that invokes Podman directly.

Point Docker API clients at the rootless socket:

bash
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"

For the rootful socket:

bash
export DOCKER_HOST="unix:///run/podman/podman.sock"

Verify the socket with curl or a Docker API client — not with docker ps from podman-docker:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/_ping
output
OK

If docker comes from podman-docker, docker ps exercises the CLI compatibility shim and talks to Podman locally. It does not prove DOCKER_HOST is set correctly. Use curl, the Docker SDK, or another actual Docker API client to verify socket access.


Run Compose with the Podman socket

Compact workflow — provider selection and compat flags live in Podman Compose:

bash
systemctl --user start podman.socket

Point API clients at the same socket your curl tests used:

bash
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"

With podman-docker installed, docker compose is not an independent Docker CLI talking to Podman over the socket. The command path is effectively docker → Podman → podman compose → an external Compose provider such as podman-compose or docker-compose. Podman sets up the environment so that provider can reach the local Podman socket.

bash
docker compose up -d

On this host that invoked podman-compose as the external provider. Verify with Podman:

bash
podman ps --filter name=socktest
output
CONTAINER ID  IMAGE                           COMMAND               CREATED        STATUS        PORTS                           NAMES
515b0a2a33a9  docker.io/library/nginx:latest  nginx -g daemon o...  1 second ago   Up 1 second   0.0.0.0:8092->80/tcp, 8080/tcp  socktest_web_1

An independently installed Docker-compatible Compose client that reads DOCKER_HOST and calls the REST API directly is a separate compatibility test from this podman-docker wrapper path.


Install and use podman-docker on RHEL

The podman-docker package provides a docker command that calls Podman:

bash
sudo dnf install podman-docker

The shim reports Podman's version string instead of Docker Engine's:

bash
docker --version
output
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
podman version 5.8.2

On RHEL 10, podman-docker also creates /var/run/docker.sock as a symlink to the rootful /run/podman/podman.sock, allowing Docker API clients that hard-code the traditional Docker socket path to reach Podman:

bash
ls -l /var/run/docker.sock
output
lrwxrwxrwx. 1 root root 23 Aug 22 16:11 /var/run/docker.sock -> /run/podman/podman.sock

That symlink targets the rootful socket. For rootless workflows, set DOCKER_HOST to the user socket instead of relying on /var/run/docker.sock. Package layout can differ on other distributions — verify paths on your host.

For CI and automation, document which socket path and which user run the client. Rootless jobs typically need systemctl --user start podman.socket, DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock, and linger enabled when the user is not logged in. Legacy scripts that hard-code /var/run/docker.sock use the rootful socket via podman-docker on RHEL.


Python Docker SDK smoke test

If docker Python package is available, a minimal client uses the same DOCKER_HOST variable:

python
import docker
client = docker.from_env()
print(client.ping())
Output

When DOCKER_HOST points at Podman's socket, ping() should return True — the same _ping route curl exercised. Pin client library versions in CI and test on staging; SDK methods that call Swarm or plugin routes will fail on Podman.


Test Docker-compatible tooling

API compatibility is per application, not universal. On this lab host:

Tool Result
curl _ping and /version Works
Python Docker SDK with DOCKER_HOST Works
docker ps via podman-docker shim Works as Podman CLI — does not test DOCKER_HOST
docker compose up via podman-docker Works via Podman Compose wrapper
/v1.40/swarm Not Found

Testcontainers, language SDKs, and CI runners each need their own staging run. A tool that only creates containers may work; one that calls Swarm, BuildKit-only routes, or dockerd plugins may not.


Common Docker API differences

Keep expectations evidence-based. On Podman 5.8.2 tested here:

  • Swarm endpoints return Not Found
  • Docker plugin and some BuildKit-specific API paths may be absent or behave differently
  • Networking defaults differ from dockerd bridge assumptions
  • Container metadata fields may not match Docker byte-for-byte

Reproduce failures against your Podman version before blaming application bugs.


Docker Swarm API is not supported

Docker-compatible container and image routes do not imply Swarm support:

text
/swarm
/services
/nodes

Docker Swarm functionality, including docker stack, is not supported.

Test on this host:

bash
curl --unix-socket "$XDG_RUNTIME_DIR/podman/podman.sock" http://d/v1.40/swarm
output
Not Found

Software that schedules services across a cluster needs Kubernetes, OpenShift, or another orchestrator — not Podman's socket API.


Socket security model

The Podman API lets clients perform Podman operations as the socket owner:

Socket Effective power
Rootless user socket Container and image control as that user — equivalent to handing them podman CLI access
Rootful socket Podman operations with root privileges

Filesystem permissions on the socket file are the main local access boundary. Do not make the socket world-writable.


Do not expose an unauthenticated TCP socket

podman system service can listen on TCP, but exposing it without TLS grants remote callers the same power as local socket access:

bash
podman system service --time 0 tcp://0.0.0.0:8080

That pattern is unsafe on a network. Prefer Unix sockets locally or SSH port forwarding remotely.

If TCP is unavoidable, use mutual TLS:

bash
podman system service --time 0 \
  --tls-cert SERVER_CERT \
  --tls-key SERVER_KEY \
  --tls-client-ca CLIENT_CA \
  tcp://HOST:PORT

This article does not walk through PKI setup — Unix sockets remain the default recommendation.


Mount the socket into another container

Some integrations run a client container that talks to the host Podman socket. Mount the appropriate socket and understand the privilege grant — the inner container gains Podman control at the socket owner's level.

On SELinux-enforcing hosts, a quick test mount may need:

text
--security-opt label=disable

That disables SELinux labeling for the container and is a security trade-off. Treat socket mounts as handing out container management on the host, not a read-only metrics endpoint.


Enable the socket at boot

Rootless socket for your user:

bash
systemctl --user enable podman.socket

For the socket to survive reboot without an interactive login:

bash
sudo loginctl enable-linger USERNAME

Linger keeps the user's systemd manager running at boot. You do not need linger merely to start the socket manually while logged in.

Rootful socket at boot:

bash
sudo systemctl enable --now podman.socket

After reboot, confirm the socket unit is active (listening) before debugging client tools.


Troubleshooting

Symptom Likely cause Fix
Cannot connect to the Docker daemon Socket not started or wrong DOCKER_HOST systemctl --user start podman.socket; echo "$DOCKER_HOST"
Permission denied on socket Client user mismatch Run client as socket owner; check ls -l on the .sock file
Tool sees root containers, expected rootless Pointed at rootful socket or /var/run/docker.sock Set DOCKER_HOST to $XDG_RUNTIME_DIR/podman/podman.sock
podman.service inactive Socket activation idle timeout Normal if podman.socket is listening — connect a client to start service
API returns Not Found for endpoint Unsupported route (Swarm, plugins) Check tool requirements; Swarm needs another orchestrator

References


Summary

Podman does not need a daemon for CLI workflows, but Docker-era tools expect a REST API over a Unix socket. podman.socket bridges that gap through systemd socket activation — the service unit may be inactive until a client connects, which is expected behavior.

Rootless and rootful sockets are not interchangeable. $XDG_RUNTIME_DIR/podman/podman.sock serves that user's store; /run/podman/podman.sock serves root's store. Set DOCKER_HOST explicitly, and on RHEL remember that /var/run/docker.sock typically symlinks to the rootful socket via podman-docker.

Test with curl against _ping, /v1.40/version, and Libpod routes before pointing production tools at the socket. Verify DOCKER_HOST with a real Docker API client — not docker ps from podman-docker, which invokes Podman directly. Compose through podman-docker uses Podman's Compose wrapper; Swarm endpoints do not work. Treat socket access as full Podman control at the owner's privilege level — use local Unix sockets, SSH forwarding, or mTLS rather than an open TCP listener.

When a tool fails, capture the exact HTTP path and compare it against what Podman documents for the compatibility layer — reinstalling Podman rarely fixes a missing Swarm or plugin route.

For migration context beyond the socket itself, see Migrate from Docker to Podman. For Compose provider choice, see Podman Compose.


Frequently Asked Questions

1. Does Podman require a daemon like Docker Engine?

Normal podman CLI commands fork a process per invocation and do not need a permanent daemon. When software expects a Docker REST API, enable podman.socket so systemd starts podman system service on demand.

2. Where is the rootless Podman socket?

After systemctl --user start podman.socket, the socket is typically at $XDG_RUNTIME_DIR/podman/podman.sock, often /run/user/UID/podman/podman.sock. Set DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock for Docker-compatible clients.

3. What is the difference between rootless and rootful Podman sockets?

They connect to different Podman stores and privilege contexts. Rootless socket operations run as your user. Rootful socket at /run/podman/podman.sock runs Podman as root. Mixing them causes confusing container lists.

4. Does Podman support Docker Swarm API endpoints?

No. Docker-compatible image and container endpoints work for many tools, but /swarm, /services, and /nodes are not implemented. Docker Swarm functionality, including docker stack, is not supported. Swarm-dependent software needs a different orchestrator.

5. Is it safe to expose podman system service on TCP without TLS?

No. The API grants Podman control as the socket owner, including arbitrary container workloads. Use Unix sockets locally, SSH forwarding remotely, or mutual TLS if TCP is unavoidable.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)