This post is about running rootless Podman containers with PostgreSQL as a central example 📦️
The post serves as an optional preparation for the next post about interacting with databases with SQLx in Rust 🦀
Landscape mode recommended on mobile devices
To restrict the scope of the post, I will assume that you know what a (rootless) Linux container is.
I will not cover Docker since there are many Docker tutorials in the internet. Just search for
docker postgres in your favorite search engine 🔎
Nevertheless, almost all of the presented Podman commands work with Docker by replacing the binary
docker at the beginning of the command.
This post is not about SQL. Therefore, the details of SQL statements will not be explained.
Before starting with Podman containers, follow the official installation guide to install Podman on your system.
Podman is preinstalled on Fedora Linux 😉
To pull and run the PostgreSQL container, run the following command in your favorite shell:
podman run \ -it \ --rm \ --name test-db \ -p 5432:5432 \ -e POSTGRES_PASSWORD=CHANGE_ME \ docker.io/library/postgres:15
Let's break it into parts:
podman runruns a container from the image specified after the options. If the image does not already exist, it will be pulled first (which is why the first run takes time with downloading).
-itruns the container with the ability to interact with it.
--rmremoves the container after exiting (read further for persistent data storage). The image is not removed.
--name test-dbspecifies the name
test-dbof the container to be able to interact with it later while it is running.
-p 5432:5432publishes the container's port
5432to the host system's port
5432. You could use something like
-p 5555:5432so that you can connect to the database on port
5555, but normally you can just keep it
5432. You can use the option
-pmore than once.
-e POSTGRES_PASSWORD=CHANGE_MEspecifies the environment variable
POSTGRES_PASSWORDinside the container. This environment variable sets the password of the default database user
postgres. You can use this option
-emore than once.
docker.io/library/postgres:15is the PostgreSQL container image from Docker Hub.
15is a tag that fetches the image with the latest PostgreSQL version
15.x.x. If you want to use the version
14, you can just replace the tag.
The command doesn't need
sudo in contrast to Docker!
This is why we are talking about rootless Podman containers which offer better security than rootful containers.
After running the command, you will see PostgreSQL starting. You can connect to the default database
localhost:5432 with the default user
postgres and the entered password.
⚠️ Warning ⚠️
To stop the container, press
CTRL+C. You will loose all the data in the database because we did not configure persistent data storage yet!
Persistent data storage
To keep the data in the database even after stopping the container, we have to mount a volume.
To do so, create the directory
data on the host system and add the following option to the
podman run command:
-v /src/path:/dest/path is an option that mounts a volume so that the container can read and persistently write into
/dest/path which is accessible by the host system in
You can use the option
-v more than once.
:Z is a SELinux flag that is needed if your host system has SELinux enabled (like in Fedora). It tells the system that this volume will not be shared with other containers.
If you do want to share a volume with other containers, you can use
:z (lowercase) instead, but this is not a good idea for a database system.
How did I know about using
/var/lib/postgresql/data as the destination directory?
Every container image has its own volumes documented in the image's documentation. For the used PostgreSQL image, the image's documentation can be found here.
Interacting with the database
Run the container again with a mounted volume from the section before.
Let's say we want to test that our data is now persistent. How can we interact with the database quickly?
To do so, open a second terminal tab and run the following command (while the container is still running):
podman exec -it test-db psql -U postgres
-itenables interactivity as with
test-dbis the name that we did specify with the
--nameoption in the
psql -U postgresis the command (with arguments) that we want to run inside the container.
After running this command, PostgreSQL will be waiting for our input:
psql (15.2 (Debian 15.2-1.pgdg110+1)) Type "help" for help. postgres=#
Let's create a table called
notes with a self incrementing
id as the primary key and a text
CREATE TABLE IF NOT EXISTS notes (id SERIAL PRIMARY KEY, note TEXT NOT NULL);
Now, verify that the table
notes was created by listing all relations with
postgres=# \dt List of relations Schema | Name | Type | Owner --------+-------+-------+---------- public | notes | table | postgres
Let's insert a row to our new table:
INSERT INTO notes (note) VALUES ('Testing containerized PostgreSQL');
Now, view all rows:
SELECT * FROM notes;
The output should be the following:
id | note ----+---------------------------------- 1 | Testing containerized PostgreSQL
Our database works 🎉 But does it store the data persistently?
CTRL+D to exit
psql. Go back to the first terminal tab and stop the container with
Now that the container is stopped, we want to verify that our data is stored persistently.
podman run command again with the volume option. Go to the second terminal tab and run the
podman exec command again and enter the following:
SELECT * FROM notes;
If you see your note "Testing containerized PostgreSQL", then your data was persistently stored 🎉
Running in the background
If you want to run a container in the background, replace
-d which stands for "detach". This way, you don't need to keep a terminal tab opened.
To verify that the container is running, run the command
podman ps. It's output should look like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 74c9ea0ac068 docker.io/library/postgres:15 postgres 2 minutes ago Up 2 minutes 0.0.0.0:5432->5432/tcp test-db
To stop a container running in the background, run
podman stop CONTAINER_NAME.
If you want to see the logs of a container running in the background, run the command
podman logs CONTAINER_NAME.
A container running in the background will not be running after a reboot! To automatically start a container after a reboot, we need a systemd service.
Automatically starting a container after a system reboot is especially important if you want to run the container on a server.
This can be achieved with a systemd service that Podman can generate for us.
Before generating a service file, we have to create a container with
podman create instead of
podman create \ --name test-db \ -p 5432:5432 \ -e POSTGRES_PASSWORD=CHANGE_ME \ -v /home/USERNAME/volumes/test-db:/var/lib/postgresql/data:Z \ docker.io/library/postgres:15
podman create creates a container without starting it.
The differences to our
podman run command are that we removed the options
--rm and used an absolute path for our volume's source path.
If you run
podman ps, then you should not find the container with the name
test-db because it is not running.
-a shows all created containers (not only the ones running). The output of
podman ps -a should look like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 889f7f2c1c23 docker.io/library/postgres:15 postgres 25 seconds ago Created 0.0.0.0:5432->5432/tcp test-db
Now, you can generate the systemd service file with the following command:
podman generate systemd test-db -fn --new
-fcreates a service file in the current directory instead of printing its content to stdout.
-nuses the name of the container in the file name instead of its ID.
--newcreates a new container instead of starting one that is already created. Since we are using a volume for persistent data, this is fine and comparable to
After running the command, you will find a file named
container-test-db.service in your current directory.
Move this service file to the user's configuration directory of systemd (create the directory if it doesn't already exist):
mv container-test-db.service ~/.config/systemd/user
Now, enable the service:
systemctl --user enable --now container-test-db.service
This service will start the container after every reboot.
--now also starts the container now instead of waiting for the next reboot.
To see the status of the container service, run
systemctl --user status container-test-db.
Since we did use user services for systemd, you have to enable the linger for our user to start the containers without the user being logged in:
Enabling the linger is very important if want the container to run on a server!
To disable the automatic restart of the container later, run
systemctl --user disable --now container-test-db.
Container images usually use environment variables for configuration.
This is also the case for the
The only required configuration variable is
POSTGRES_PASSWORD which we did already use.
But there are other configuration variables that can be found in the documentation of the container image.
I hope that you found this short introduction to rootless Podman helpful.
Now that we know how to run a PostgreSQL container, we can connect to such a database with SQLx in Rust. This will be the topic of the next post 😃