Skip to content

Installation with Podman

MrJSA edited this page Oct 28, 2026 · 3 revisions

This guide installs the shop with Podman instead of Docker and uses Caddy on the server as the HTTPS reverse proxy. At the end you have the shop at https://shop.example.com and the admin at https://admin.example.com, with automatic certificates from Let's Encrypt.

Podman runs here rootful (as root, via sudo): Caddy needs ports 80/443 and the updater needs the Podman socket. The shop's docker-compose.yml is used unchanged; the Docker Compose program talks to Podman's Docker-compatible socket.

Replace example.com with your own domain throughout.

1. Requirements

Same as in Installation (2 GB RAM, 10 GB disk, two DNS records, ports 22/80/443), plus a distribution with Podman 4.9 or newer: Ubuntu 24.04, Debian 13, Fedora 40+ or RHEL/AlmaLinux/Rocky 9.4+. Older Podman versions lack the container DNS the services use to find each other.

Set up the DNS records as described in Installation#2-dns.

2. Install Podman, git and Caddy

Ubuntu / Debian:

sudo apt update
sudo apt install -y podman netavark aardvark-dns git curl caddy
podman --version   # must be 4.9 or newer

Fedora / RHEL family:

sudo dnf install -y podman netavark aardvark-dns git curl caddy

(If your distribution has no caddy package, use the official repository: https://caddyserver.com/docs/install.)

3. Let Podman stand in for Docker

Podman socket, plus the usual Docker socket path pointing to it (the shop's updater expects /var/run/docker.sock):

sudo systemctl enable --now podman.socket
echo 'L+ /run/docker.sock - - - - /run/podman/podman.sock' | sudo tee /etc/tmpfiles.d/podman-docker-socket.conf
sudo systemd-tmpfiles --create /etc/tmpfiles.d/podman-docker-socket.conf

Image names like postgres:16-alpine come from Docker Hub:

sudo mkdir -p /etc/containers/registries.conf.d
printf 'unqualified-search-registries = ["docker.io"]\nshort-name-mode = "permissive"\n' | sudo tee /etc/containers/registries.conf.d/10-docker-io.conf

Start containers after a reboot (Podman has no always-running daemon that does this):

sudo systemctl enable podman-restart.service

Docker Compose (the program only, no Docker engine):

sudo curl -fSL "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo docker-compose version   # prints v2.29.7

On other wiki pages, where a command says docker compose …, use sudo docker-compose … instead.

Firewall: let containers use Podman's DNS

The shop's containers find each other by name (the backend connects to db). Podman answers these names with a small DNS server (aardvark-dns) that runs on the server itself, on the gateway of each container network (e.g. 10.89.0.1). A firewall that denies incoming traffic (ufw default deny incoming, the usual setup) also blocks the containers from asking it: the backend cannot find the database and restarts endlessly. Unlike Docker, Podman does not open this by itself.

ufw (Ubuntu / Debian), even if you only enable ufw later. Allow DNS, and only DNS, from Podman's networks (podman+ matches all of them: podman0, podman1, …):

sudo ufw allow in on podman+ to any port 53 comment 'Podman container DNS'

Nothing becomes reachable from the internet: podman+ are internal interfaces, not the public network card.

firewalld (Fedora / RHEL): usually nothing to do. Only if the test below fails:

sudo firewall-cmd --permanent --zone=trusted --add-source=10.89.0.0/16
sudo firewall-cmd --reload

Check container DNS and internet access

Two throw-away containers on a test network, like the shop uses: one must find the other by name and reach the internet (the build downloads packages).

sudo podman network create dnstest
sudo podman run -d --name dnstest-a --network dnstest docker.io/library/alpine:3.20 sleep 120
sudo podman run --rm --network dnstest docker.io/library/alpine:3.20 \
  sh -c 'nslookup dnstest-a >/dev/null && echo "container DNS OK"; wget -q --spider https://github.com && echo "internet OK"'
sudo podman rm -f dnstest-a
sudo podman network rm dnstest

Both container DNS OK and internet OK must be printed.

  • container DNS OK missing: the firewall rule above is missing (sudo ufw status | grep 53), or the network backend is not netavark (sudo podman info --format '{{.Host.NetworkBackend}}').

  • internet OK missing (hangs, bad address, timed out): ufw blocks container traffic to the internet. Allow it and run the test again:

    sudo ufw default allow routed
    sudo ufw reload

    This only lets containers connect out; the shop's ports still listen on 127.0.0.1 only.

4. Download the shop

sudo git clone https://github.com/MrJSA/RustCraft.git /opt/rustwebshop
cd /opt/rustwebshop
sudo git checkout "$(sudo git tag -l 'v*' | grep -v -- -beta | sort -V | tail -1)"   # newest stable release

Why "rustwebshop" and not "RustCraft"? RustCraft used to be called rustwebshop. The old name stays as the folder name and as the stack name in docker-compose.yml (name: rustwebshop), so containers, networks and volumes are called rustwebshop-… / rustwebshop_… (e.g. rustwebshop_pgdata for the database). That is intended: renaming the stack would make existing installations start with new, empty volumes, i.e. without their database and uploads. Do not change the name in docker-compose.yml; the updater refuses to update an installation with local code changes.

5. Configure

sudo cp .env.example .env
sudo chmod 600 .env
sudo nano .env

Set at least:

DB_PASSWORD=<output of: openssl rand -hex 24>

SHOP_PUBLIC_URL=https://shop.example.com
ADMIN_PUBLIC_URL=https://admin.example.com

# Caddy runs on the server itself: no built-in proxy, the shop ports only listen locally
COMPOSE_PROFILES=
PUBLISH_ADDR=127.0.0.1

Another website on this server already uses port 8080? Add STOREFRONT_PORT=8090 (any free port) and use 127.0.0.1:8090 instead of 127.0.0.1:8080 in the Caddyfile and the checks below. ADMIN_PORT (4000) can be changed the same way; the backend additionally listens on 127.0.0.1:8081.

Podman needs two adjustments that Docker does not. Create /opt/rustwebshop/docker-compose.override.yml (it is not part of the repository, so updates never touch it):

sudo tee /opt/rustwebshop/docker-compose.override.yml > /dev/null <<'YAML'
# Podman: classic image builds (Podman has no BuildKit) and no SELinux labels on the
# Podman socket and project folder that the updater works with
services:
  updater:
    environment:
      DOCKER_BUILDKIT: "0"
    security_opt:
      - label=disable
  repo-move:
    security_opt:
      - label=disable
YAML

6. Start

cd /opt/rustwebshop
sudo DOCKER_BUILDKIT=0 docker-compose up -d --build   # first build takes 5–15 minutes
sudo docker-compose ps                                 # all services "running"/"healthy"
sudo docker-compose logs backend | grep "Initial admin"   # one-time admin password

repo-move is a one-time helper that has already finished, so ps does not list it.

No Initial admin line? The backend may still be starting: wait 30 seconds and try again. Still nothing? Run sudo docker-compose logs --tail 20 backend. If it says Failed to connect to PostgreSQL … pool timed out, the backend cannot resolve db: add the firewall rule from step 3, then sudo docker-compose restart backend.

Check locally that both answer before you set up Caddy:

curl -sI http://127.0.0.1:8080 | head -1   # shop
curl -sI http://127.0.0.1:4000 | head -1   # admin (a redirect to /login is fine)

7. Caddy as reverse proxy

Replace /etc/caddy/Caddyfile with:

shop.example.com {
	encode zstd gzip
	request_body {
		max_size 50MB
	}
	reverse_proxy 127.0.0.1:8080
}

admin.example.com {
	encode zstd gzip
	request_body {
		max_size 50MB
	}
	reverse_proxy 127.0.0.1:4000
}
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl enable caddy
sudo systemctl reload caddy

Caddy gets the certificates on the first request. Open https://admin.example.com, log in as admin with the initial password and choose your own (min. 12 characters).

Optional: allow the admin only from certain IP addresses by adding this at the top of the admin.example.com block:

	@outside not remote_ip 203.0.113.0/24 198.51.100.7
	respond @outside 403

8. Firewall

Ubuntu / Debian (ufw):

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw enable

Fedora / RHEL (firewalld):

sudo firewall-cmd --permanent --add-service=http --add-service=https --add-service=http3
sudo firewall-cmd --reload

Do not open 8080, 4000 or 8081. Keep the Podman DNS rule from step 3: ufw rules are only added, never replaced, so the commands above leave it in place.

Updates

Settings → System & Updates works the same as with Docker (see Updates and Release Channels). The updater controls Podman through the socket from step 3.

If an update from the admin fails while building, update manually:

cd /opt/rustwebshop
sudo mkdir -p backups
sudo docker-compose exec -T db pg_dump -U shop_user shop_db | sudo tee backups/manual-$(date +%F).sql > /dev/null
sudo git fetch --tags && sudo git checkout "$(sudo git tag -l 'v*' | grep -v -- -beta | sort -V | tail -1)"
sudo DOCKER_BUILDKIT=0 docker-compose up -d --build

Domains

Do not use Settings → System & Updates → Domains in the admin: it is meant for the built-in proxy and would start it, which then fights with the server's Caddy over ports 80/443. With Caddy on the server, change the domains in /etc/caddy/Caddyfile and in SHOP_PUBLIC_URL / ADMIN_PUBLIC_URL in .env, then:

sudo systemctl reload caddy
cd /opt/rustwebshop && sudo docker-compose up -d

Troubleshooting

Problem Check
Backend restarts endlessly with Failed to connect to PostgreSQL … pool timed out, no Initial admin line The firewall blocks container DNS: add the rule from step 3, then sudo docker-compose restart backend
Services cannot reach each other (name not resolved, bad address 'db') First the firewall rule from step 3 (sudo ufw status | grep 53). Then sudo podman info --format '{{.Host.NetworkBackend}}' must print netavark and aardvark-dns must be installed; afterwards sudo docker-compose down && sudo docker-compose up -d
Build fails downloading packages (npm, cargo, apk) Containers cannot reach the internet: see the test in step 3 (ufw default allow routed)
Build stops with Killed or signal: 9 Out of memory: add swap (see Installation#1-requirements), then start again
address already in use on 8080 or 4000 Another service uses the port: set STOREFRONT_PORT / ADMIN_PORT in .env (step 5)
502 Bad Gateway The shop is not running: sudo docker-compose ps, curl -sI http://127.0.0.1:8080
short-name ... did not resolve The registries file from step 3 is missing
"Updater not reachable" or permission denied on docker.sock ls -l /run/docker.sock must point to /run/podman/podman.sock; systemctl status podman.socket
Build fails with a BuildKit/buildx error Start with DOCKER_BUILDKIT=0 and keep the override file from step 5
Shop gone after a reboot sudo systemctl enable podman-restart.service
No certificate DNS points to the server? Ports 80/443 open? journalctl -u caddy -e

Everything else: Troubleshooting.

Next

Continue with First Steps, then switch on Backups.

Clone this wiki locally