iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🦭

Running PostgreSQL Containers with systemd Using Podman Quadlet

に公開

Introduction

By using Quadlet, which has been available since Podman v4.6, you can easily start containers with systemd. Previously, you could generate unit files using the podman generate systemd command, but Quadlet makes it even easier to manage containers under systemd.

For details on the benefits, please refer to the Red Hat Enterprise Linux 9 documentation: "15.4. Advantages of using Quadlets over the podman generate systemd command".

Preparation

Creating a Directory for Unit Files

Create a directory to place the Quadlet files.

mkdir -p $HOME/.config/containers/systemd

If XDG_CONFIG_HOME is set, use that instead.

mkdir -p $XDG_CONFIG_HOME/containers/systemd

Creating a Directory for Volumes

To persist PostgreSQL data, bind the data inside the container to the host-side filesystem. For example, create a directory at a location like this:

sudo mkdir -p /opt/data/podman/systemd/postgresql-server
sudo chown ${USER}: /opt/data/podman/systemd/postgresql-server

Of course, creating it under your home directory is also perfectly fine.

Creating Unit Files

Creating the .container File

Create a unit file named postgresql-server.container inside the unit file directory.

$HOME/.config/containers/systemd/postgresql-server.container
[Unit]
Description=PostgreSQL server container
After=local-fs.target

[Container]
Image=docker.io/library/postgres:18
Environment=POSTGRES_PASSWORD=postgrespwd
Environment=PGDATA=/var/lib/pgsql
Volume=/opt/data/podman/systemd/postgresql-server/pgsql:/var/lib/pgsql:Z
PublishPort=5432:5432

[Install]
WantedBy=default.target


In the [Container] section, you describe settings related to the container.

In this example, we use the official PostgreSQL image from Docker Hub. This image uses the POSTGRES_PASSWORD environment variable to set the default superuser password during the initial startup, so the password is hardcoded for the first run.

The default PGDATA for the docker.io/library/postgres:18 image is /var/lib/postgresql/data, but in this case, we set a different directory and bind it to the host-side filesystem.

Use the PublishPort= directive to expose ports to the host. The format is host:container.

Other environment variables can also be set as needed.

Generating the .service File

Generate the postgresql-server.service file from the postgresql-server.container file.

systemctl --user daemon-reload

To confirm that it has been placed under systemd management, let's check the status of postgresql-server.service.

systemctl --user status postgresql-server.service
output
○ postgresql-server.service - PostgreSQL server container
     Loaded: loaded (/home/lillno/.config/containers/systemd/postgresql-server.container; generated)
    Drop-In: /usr/lib/systemd/user/service.d
             └─10-timeout-abort.conf
     Active: inactive (dead)

The service is still inactive because it hasn't been started yet, but you can confirm that it is managed by systemd.

Starting the Service

Optional: Pulling the Image

The docker.io/library/postgres:18 image is pulled during the first startup, but you can also pull it in advance.

podman pull docker.io/library/postgres:18

Starting the postgresql-server Service

Start the postgresql-server service.

systemctl --user start postgresql-server.service

Starting PostgreSQL on OS Boot

Verify the connection using psql(1). The default username is postgres (this can be configured with the POSTGRES_USER environment variable during initial startup).

psql --host=localhost --username=postgres --password

Once the connection is confirmed, enable the postgresql-server.service.

When using the Quadlet mechanism, the .service file is a temporary artifact, so you cannot use the systemctl command to enable it. Although you cannot use enable, Quadlet sets it to a state equivalent to being enabled at the time of systemctl daemon-reload.

On the other hand, since systemctl is run with --user, the service starts upon user login and stops upon logout. To start the service at OS boot, use loginctl(1).

loginctl enable-linger ${USER}

Removing the Password from the .container File

The postgresql-server.container file created earlier had a hardcoded password, but since it is only used during the database initialization (initdb) process at the first startup, we will remove it from the file to improve security.

$HOME/.config/containers/systemd/postgresql-server.container
[Unit]
Description=PostgreSQL server container
After=local-fs.target

[Container]
Image=docker.io/library/postgres:18
Environment=PGDATA=/var/lib/pgsql
Volume=/opt/data/podman/systemd/postgresql-server/pgsql:/var/lib/pgsql:Z
PublishPort=5432:5432

[Install]
WantedBy=default.target


Afterwards, have systemd reload the files and restart the service.

systemctl --user daemon-reload
systemctl --user restart postgresql-server.service

Bonus

Permissions for the Bound Directory

Since it is running in Rootless Podman, you do not have permissions to access the directory bound to the host filesystem directly. This is the correct behavior and reflects the secure state of Rootless Podman.

ls /opt/data/podman/systemd/postgresql-server/pgsql
output
ls: cannot open directory '/opt/data/podman/systemd/postgresql-server/pgsql': Permission denied

If you want to perform operations with host-side general user permissions, use Podman's unshare.

podman unshare ls /opt/data/podman/systemd/postgresql-server/pgsql
output
PG_VERSION    pg_hba.conf    pg_replslot   pg_subtrans  postgresql.auto.conf
base          pg_ident.conf  pg_serial     pg_tblspc    postgresql.conf
global        pg_logical     pg_snapshots  pg_twophase  postmaster.opts
pg_commit_ts  pg_multixact   pg_stat       pg_wal       postmaster.pid
pg_dynshmem   pg_notify      pg_stat_tmp   pg_xact

Handling Sensitive Information like Passwords in Podman

In this case, since the password was only needed temporarily, we hardcoded it directly, but this is not very desirable from a security perspective.

Podman has a feature called Secrets. If you need to use sensitive information such as passwords or API keys permanently, consider adopting the Secrets feature.

Discussion