{"id":167800,"date":"2026-05-20T23:02:33","date_gmt":"2026-05-20T20:02:33","guid":{"rendered":"https:\/\/computingforgeeks.com\/?p=167800"},"modified":"2026-05-21T01:40:42","modified_gmt":"2026-05-20T22:40:42","slug":"podman-compose-fedora","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/podman-compose-fedora\/","title":{"rendered":"Install Podman Compose on Fedora 44 \/ 43 \/ 42"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Podman has been the default container runtime on Fedora Server and Workstation for years. Podman Compose is the matching multi-container orchestrator: write a single <code>compose.yaml<\/code>, run <code>podman-compose up<\/code>, get a stack of containers that talk to each other through a private network. The interface is the same Compose spec the Docker world uses, so existing <code>docker-compose.yml<\/code> files run unchanged on Fedora.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide installs Podman, podman-compose, and the Docker compatibility shim on Fedora, then walks a real two-service stack (Caddy web server plus Redis) from the compose file through <code>up<\/code>, <code>ps<\/code>, <code>logs<\/code>, and <code>down<\/code>. Every command was executed on a real Fedora install and every output you see in the screenshots is what your terminal will show.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Podman Compose on Fedora<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two reasons it is the better default on Fedora:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Rootless by default.<\/strong> Containers run as your user, not as root. A breakout from a container is a breakout into your user account, not the system. Docker on Linux still requires the user to be in the docker group, which is effectively root equivalent.<\/li>\n\n\n\n<li><strong>No daemon.<\/strong> Podman shells out per command instead of running a long-lived dockerd. Containers survive when your shell exits and they stop cleanly when their cgroup goes away. There is no second moving part to keep healthy.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Podman Compose is the orchestration layer. It parses Compose files (v2 and v3 spec), translates them into Podman commands, and manages the lifecycle of the stack. Its CLI mirrors Docker Compose so muscle memory carries across.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install Podman and Podman Compose<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">All three packages are in the official Fedora repository. Install the Docker compatibility shim too, so existing scripts that call <code>docker<\/code> still work:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>sudo dnf install -y podman podman-compose podman-docker<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Verify the versions:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>podman --version\npodman-compose --version\nrpm -q podman podman-compose podman-docker<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Enable the rootless Podman socket if you intend to use docker-compatible clients that talk over the socket:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>systemctl --user enable --now podman.socket\necho \"DOCKER_HOST=unix:\/\/\/run\/user\/$(id -u)\/podman\/podman.sock\" &gt;&gt; ~\/.bashrc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The socket lets tools like the VS Code Containers extension and Docker Desktop replacements talk to rootless Podman as if it were Docker.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Write a real compose.yaml<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Put a two-service stack together: Caddy as the web front, Redis as a back-end key-value store. Both speak through the implicit network Compose creates between services.<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>mkdir -p ~\/cfg-compose-demo &amp;&amp; cd ~\/cfg-compose-demo\n# Write compose.yaml (paste the YAML below into the file with your editor)\n$EDITOR compose.yaml\n# Write Caddyfile alongside it\n$EDITOR Caddyfile<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>compose.yaml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>services:\n  caddy:\n    image: docker.io\/library\/caddy:2-alpine\n    container_name: caddy-demo\n    ports:\n      - \"8080:80\"\n    volumes:\n      - .\/Caddyfile:\/etc\/caddy\/Caddyfile:Z\n      - caddy_data:\/data\n    restart: unless-stopped\n  redis:\n    image: docker.io\/library\/redis:7-alpine\n    container_name: redis-demo\n    restart: unless-stopped\nvolumes:\n  caddy_data:<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Caddyfile<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>:80 {\n    respond \"Hello from Podman Compose on Fedora\"\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>:Z<\/code> mount flag on the Caddyfile relabels the file with the right SELinux context so the container can read it. Skip that on a non-SELinux host; keep it on Fedora.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bring the stack up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One command pulls the images and starts both containers in the background:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>podman-compose up -d<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-up-fedora.png\" alt=\"podman-compose up Caddy and Redis stack from compose.yaml on Fedora\" class=\"wp-image-167794\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-up-fedora.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-up-fedora-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-up-fedora-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Behind the scenes Podman created a network named <code>cfg-compose-demo_default<\/code>, attached both containers to it, registered the named volume <code>caddy_data<\/code>, and mapped host port 8080 to the Caddy container&#8217;s port 80.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verify the stack is serving traffic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Inspect the running containers and hit the published port:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>podman-compose ps\ncurl -s http:\/\/localhost:8080\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The command output is shown above.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"800\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-verify-fedora.png\" alt=\"podman-compose ps curl localhost Caddy Redis on Fedora\" class=\"wp-image-167795\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-verify-fedora.png 920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-verify-fedora-300x261.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/05\/wm-podman-compose-verify-fedora-768x668.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Plain Podman commands work against the same containers; <code>podman ps<\/code>, <code>podman logs<\/code>, and <code>podman exec<\/code> are all available because Podman Compose only creates regular containers, not Compose-specific objects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common compose operations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The day-to-day commands you will run most often:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code># Tail logs for one service\npodman-compose logs -f caddy\n\n# Restart a single service\npodman-compose restart caddy\n\n# Re-pull images and recreate containers in one shot\npodman-compose pull\npodman-compose up -d --force-recreate\n\n# Open a shell in a running container\npodman-compose exec caddy sh\n\n# Stop everything, keep volumes\npodman-compose down\n\n# Stop everything and remove named volumes\npodman-compose down -v<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Every flag here is identical to Docker Compose, so any existing playbook or Makefile keeps working.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SELinux, volumes, and the :Z gotcha<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Fedora ships SELinux in enforcing mode. Without the right label, bind-mounted host directories fail at runtime with cryptic permission-denied errors. Two flags handle it:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>:Z<\/code>: relabels the host path with a private container label. Use this when the bind mount is dedicated to one container.<\/li>\n\n\n\n<li><code>:z<\/code> (lowercase): shared label, lets multiple containers read the same host directory. Useful for shared data dirs.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Without one of those, the container sees an empty or permission-denied mount on first start. The fix is in the compose file, not in <code>setenforce 0<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Run the stack as a systemd service<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For a stack that must come back after reboot, wrap Compose in a systemd user service. The simplest pattern uses the Compose file directly:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>mkdir -p ~\/.config\/systemd\/user\n$EDITOR ~\/.config\/systemd\/user\/cfg-stack.service\n# (paste the unit body below into the file)\n\nsystemctl --user daemon-reload\nsystemctl --user enable --now cfg-stack\nloginctl enable-linger \"$USER\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>~\/.config\/systemd\/user\/cfg-stack.service<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code code\"><code>&#91;Unit]\nDescription=cfg compose demo stack\nAfter=default.target\n\n&#91;Service]\nType=simple\nWorkingDirectory=%h\/cfg-compose-demo\nExecStart=\/usr\/bin\/podman-compose up\nExecStop=\/usr\/bin\/podman-compose down\nRestart=on-failure\n\n&#91;Install]\nWantedBy=default.target<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>loginctl enable-linger<\/code> step is the one most guides skip. Without it, the user systemd manager shuts down when you log out, taking the stack with it. With it, your user containers keep running between logins.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For more production-shaped workloads, look at Quadlet (Podman 4.4 plus) which generates first-class systemd units from a small declarative file. The pattern fits especially well for single-container services and is the recommended path on Fedora for long-running services.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Migrating from Docker Compose<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Existing <code>docker-compose.yml<\/code> files generally drop in unchanged. The few real differences worth knowing:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Image registries<\/strong>: Podman defaults to short-name resolution from <code>\/etc\/containers\/registries.conf<\/code>. Use the full image path (<code>docker.io\/library\/nginx<\/code>, not <code>nginx<\/code>) in production compose files to avoid surprises.<\/li>\n\n\n\n<li><strong>Volume drivers<\/strong>: most named volume features are supported. Anything that depends on the Docker volume plugin API will not work; use a host bind mount instead.<\/li>\n\n\n\n<li><strong>Healthchecks<\/strong>: <code>healthcheck<\/code> stanzas work but only Podman 5 reports them in <code>ps<\/code> output.<\/li>\n\n\n\n<li><strong>User namespaces<\/strong>: rootless Podman maps container UID 0 to your host UID by default. Bind-mounted files inside the container appear owned by root. Use <code>--userns=keep-id<\/code> per service (or compose-level <code>userns_mode: \"keep-id\"<\/code>) when this matters.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Where to go next<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This guide is part of the Fedora 44 Workstation series. For an interactive container workflow rather than a service stack, the <a href=\"https:\/\/computingforgeeks.com\/install-distrobox-toolbox-fedora\/\">Install Distrobox and Toolbox on Fedora<\/a> guide covers the development-environment side of Podman: cross-distro containers with home and GPU passthrough. For non-Fedora hosts the same Podman Compose patterns translate directly to <a href=\"https:\/\/computingforgeeks.com\/install-podman-buildah-rocky-almalinux\/\">Rocky Linux 10 and AlmaLinux 10<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Podman has been the default container runtime on Fedora Server and Workstation for years. Podman Compose is the matching multi-container orchestrator: write a single compose.yaml, run podman-compose up, get a stack of containers that talk to each other through a private network. The interface is the same Compose spec the Docker world uses, so existing &#8230; <a title=\"Install Podman Compose on Fedora 44 \/ 43 \/ 42\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/podman-compose-fedora\/\" aria-label=\"Read more about Install Podman Compose on Fedora 44 \/ 43 \/ 42\">Read more<\/a><\/p>\n","protected":false},"author":15,"featured_media":167788,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,299,47,50],"tags":[218,681,4449,38646,39862],"cfg_series":[39847],"class_list":["post-167800","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-fedora","category-how-to","category-linux","category-linux-tutorials","tag-containers","tag-fedora","tag-podman","tag-podman-compose","tag-rootless","cfg_series-fedora-44-workstation"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167800","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=167800"}],"version-history":[{"count":4,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167800\/revisions"}],"predecessor-version":[{"id":167814,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/167800\/revisions\/167814"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/167788"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=167800"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=167800"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=167800"},{"taxonomy":"cfg_series","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/cfg_series?post=167800"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}