Podman deployment
Run Prelude Collector under Podman, including rootless considerations, SELinux labels, and Quadlet/systemd integration.
This page is the reference for running Prelude Collector under Podman. Podman is a daemonless, rootless OCI runtime that is mostly drop-in compatible with Docker - the same image and the same Compose file work, with a handful of differences called out below.
For the matching Docker reference, see Docker deployment. For every config field the collector accepts, see the Configuration reference.
Prerequisites
| Requirement | Version | Notes |
|---|---|---|
| Podman | 4.0 or later | Includes the podman compose subcommand. |
| podman-compose | 1.0 or later | Optional, only if you prefer the standalone tool. |
| RAM | 2 GB minimum | Same as Docker. |
The collector image is published to the public registry
registry.arolo-solutions.com/prelude/prelude-collector. The
examples below use the :1.0 tag, the current stable release.
Pull the image
The registry is publicly pullable — no authentication required:
podman pull registry.arolo-solutions.com/prelude/prelude-collector:1.0.0
Quick start with Compose
The same docker-compose.yml shown in the
Docker deployment works under Podman with no changes for
most workflows:
podman compose up -d
# or, if you use the standalone tool:
podman-compose up -d
The schema migrates automatically on startup and a default admin
(admin / @rolo!Pass246) is seeded on first boot — sign in on the
web UI and change the password. Then issue an API token (with
COLLECTOR_AUTO_MIGRATE=false, run migrate up first):
podman compose exec collector ./prelude-collector user token \
-u admin -d api-token
Standalone (without Compose)
podman run -d \
--name prelude-collector \
--restart unless-stopped \
-p 4030:4030 \
-e COLLECTOR_DB_HOST=your-postgres-host \
-e COLLECTOR_DB_PORT=5432 \
-e COLLECTOR_DB_USER=prelude \
-e COLLECTOR_DB_PASSWORD=change-me-sE43kapqD8df5fds \
-e COLLECTOR_DB_NAME=collector \
-v collector-data:/app/storage:Z \
registry.arolo-solutions.com/prelude/prelude-collector:1.0.0
Rootless differences
Podman runs rootless by default. There are three differences from Docker worth knowing about.
Port binding below 1024
Rootless containers cannot bind to privileged ports. The default
collector port 4030 is fine. If you need to bind 80 or 443,
either lower the unprivileged port floor on the host or front the
collector with a reverse proxy:
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
SELinux volume labels
On SELinux-enforcing distributions (RHEL, Fedora, Rocky, CentOS
Stream), bind-mounted host paths need a :z or :Z suffix so
Podman relabels them. The quick-start stack uses a named volume for
storage, but if you bind-mount a host directory instead, relabel it:
services:
collector:
volumes:
- ./storage:/app/storage:Z
Use :z if the volume is shared between containers, :Z if it is
private to one container.
Inter-container DNS
Podman uses its own DNS resolver for inter-container name lookup. If service names do not resolve out of the box, define a named network explicitly and attach the services to it:
podman network create prelude
networks:
default:
name: prelude
external: true
Quadlet and systemd
For production Podman deployments, manage containers as systemd
units through Quadlet rather than running Compose under a shell.
Quadlet reads .container files and generates systemd units at
boot, which gives you proper start ordering, restart policy, and
journal logging.
Place unit files under ~/.config/containers/systemd/ for rootless
or /etc/containers/systemd/ for rootful.
Network
[Network]
NetworkName=prelude
PostgreSQL
[Container]
ContainerName=prelude-postgres
Image=docker.io/library/postgres:16-alpine
Environment=POSTGRES_USER=prelude
Environment=POSTGRES_PASSWORD=change-me-sE43kapqD8df5fds
Environment=POSTGRES_DB=collector
PublishPort=5432:5432
Volume=prelude-pgdata.volume:/var/lib/postgresql/data
Network=prelude.network
[Service]
Restart=always
[Install]
WantedBy=default.target
NATS
[Container]
ContainerName=prelude-nats
Image=docker.io/library/nats:2
PublishPort=4222:4222
Network=prelude.network
[Service]
Restart=always
[Install]
WantedBy=default.target
Collector
[Container]
ContainerName=prelude-collector
Image=registry.arolo-solutions.com/prelude/prelude-collector:1.0.0
PublishPort=4030:4030
Environment=COLLECTOR_DB_HOST=prelude-postgres
Environment=COLLECTOR_DB_PORT=5432
Environment=COLLECTOR_DB_USER=prelude
Environment=COLLECTOR_DB_PASSWORD=change-me-sE43kapqD8df5fds
Environment=COLLECTOR_DB_NAME=collector
Volume=prelude-storage.volume:/app/storage:Z
Network=prelude.network
[Service]
Restart=always
After=prelude-postgres.service prelude-nats.service
[Install]
WantedBy=default.target
Manage with systemd
# Pick up new unit files
systemctl --user daemon-reload
# Start the stack
systemctl --user start prelude-postgres prelude-nats prelude-collector
# Enable at boot (rootless needs lingering)
loginctl enable-linger $USER
systemctl --user enable prelude-postgres prelude-nats prelude-collector
# Status and logs
systemctl --user status prelude-collector
journalctl --user -u prelude-collector -f
For rootful Quadlet, drop --user and place the units under
/etc/containers/systemd/.
Networking and ports
Same as Docker - see the port table
on the Docker page. Inside a Podman network, services resolve each
other by container name (postgres, nats).
Volumes
Same as Docker - see Volumes on the Docker page. Remember the SELinux label note above when bind-mounting on RHEL or Fedora hosts.
Podman vs Docker quick reference
| Operation | Docker | Podman |
|---|---|---|
| Start stack | docker compose up -d |
podman compose up -d |
| View logs | docker compose logs -f |
podman compose logs -f |
| Exec into container | docker compose exec collector sh |
podman compose exec collector sh |
| Pull images | docker compose pull |
podman compose pull |
| Auto-start at boot | Docker daemon enabled | Quadlet + systemctl enable |
| Rootless by default | No | Yes |
| SELinux volume labels | Not needed | :z or :Z on bind mounts |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Cannot resolve service name | Default network lacks DNS | Create an explicit Podman network and attach services to it |
Permission denied on a bind mount |
SELinux blocking access | Add :z or :Z to the volume |
| Port already in use | Rootless port conflict | podman port -a to inspect, change the host port |
| Containers don't start at boot | Lingering not enabled | loginctl enable-linger $USER, then re-enable the units |
Air-gapped environments
For an air-gapped Podman host, transfer the image with podman save
and podman load. See the
Air-gapped install tutorial for
the step-by-step, and Air-gap deployment for the
operational reference.
See also
- Docker deployment - same compose file, Docker specifics.
- Configuration reference - every
COLLECTOR_*environment variable. - Air-gap deployment - operational shape of an air-gapped install.
- Air-gapped install tutorial - step-by-step walkthrough.