PostgreSQL is a powerful, open-source relational database system widely used in modern applications. Running PostgreSQL as a container offers a range of benefits, particularly when using Podman, a rootless container engine. This post will guide you through the steps to set up PostgreSQL instances using Podman and systemd.
Why Run PostgreSQL as a Container?
Running PostgreSQL as a container has several advantages:
- Isolation: Each container runs independently, ensuring that libraries and dependencies are isolated from the host system and other containers.
- Portability: Containers can be easily moved between environments, such as development, staging, and production.
- Quick Deployment: Starting a new PostgreSQL instance is as simple as running a container, reducing setup time.
- Simplified Configuration: Containerized PostgreSQL uses environment variables for easy configuration.
- Rootless Execution: Podman allows running containers without requiring root privileges, enhancing security.
Advantages of Using Podman with Systemd
Integrating Podman containers with systemd adds the following benefits:
- Automatic Startup: Containers can be configured to start automatically with the system.
- Service Management: Systemd enables familiar service management commands like
start
,stop
, andstatus
. - Reliability: Systemd can restart failed containers automatically.
- Resource Isolation: With cgroups, systemd can manage resource limits for containers.
Disclaimer
The steps and configurations provided in this article are intended for demonstration and educational purposes only. Users must carefully consider the following before deploying PostgreSQL containers in any environment:
- Storage Backend: Ensure proper planning and management of storage backend and volume data, just like any critical application storage system.
- System Resources: Evaluate the system’s resource capacity (CPU, memory, disk I/O) before deploying multiple container instances on a single machine to avoid resource exhaustion.
- Production Readiness: This is an opinionated approach, and it is the user’s responsibility to implement all necessary production-level configurations, including security, monitoring, and high availability.
- Sensitive Information: Handle sensitive information, such as passwords and credentials, with care. Avoid hardcoding credentials in container definitions, scripts, or environment files.
The author is not responsible for any data loss, performance issues, or security vulnerabilities arising from improper implementation of the concepts discussed in this article. Proceed at your own discretion and ensure thorough testing before deploying to production environments.
Setting Up PostgreSQL as a Container with Podman
Follow these steps to run PostgreSQL instances with Podman and systems. If you are installing Podman for the first time, refer to this guide – Installing Podman on Red Hat Enterprise Linux 9.
Step 1: Pull the PostgreSQL Container Image
The PostgreSQL image from the container registry provides a secure and optimized database engine. Pull the image:
podman pull postgres:latest
Alternatively, you can use the official image registry.redhat.io/rhel8/postgresql-15:latest
from Red Hat’s container registry which provides a secure and optimized database engine. Remember, you need to authenticate before pulling an image from Red Hat container registry.
Step 2: Run the First PostgreSQL Container
Alternatively, you can use the podman-compose
command to quickly spin up containers with volumes and networks without worrying about command line arguments as shown in the following sections. Refer to the sample podman-compose.yaml
file in this repository.
Step 2.1: Create a volume for the PostgreSQL server
If you don’t specify a volume, the PostgreSQL data stored in /var/lib/postgresql/data/
will be lost once the container is stopped or removed. To ensure data persistence, it’s essential to mount a volume for the PostgreSQL data store. You can either use a local directory from your host system or create a dedicated container volume to store the data securely.
Note: You can also mount the local directory path to the container without creating a Podman volume if there is a requirement.
Create a container volume as follows.
$ podman volume create postgresapp1
postgresapp1
Verify the volume details.
$ podman volume inspect postgresapp1
[
{
"Name": "postgresapp1",
"Driver": "local",
"Mountpoint": "/home/devops/.local/share/containers/storage/volumes/postgresapp1/_data",
"CreatedAt": "2025-01-07T13:32:15.964258532Z",
"Labels": {},
"Scope": "local",
"Options": {},
"MountCount": 0,
"NeedsCopyUp": true,
"NeedsChown": true,
"LockNumber": 36
}
]
Step 2.2: Create a secret to store PostgreSQL admin password
It is possible to pass the sensitive information via environment variables but we are using the Podman secret to store the PostgreSQL admin password as follows.
$ echo "postgresadmin" | podman secret create postgresql_app1_admin_password -
Verify the secret using to ensure the secret it created.
$ podman secret inspect postgresql_app1_admin_password
[
{
"ID": "0dd142faa2448d107d8849145",
"CreatedAt": "2025-01-07T13:27:10.295864132Z",
"UpdatedAt": "2025-01-07T13:27:10.295864132Z",
"Spec": {
"Name": "postgresql_app1_admin_password",
"Driver": {
"Name": "file",
"Options": {
"path": "/home/devops/.local/share/containers/storage/secrets/filedriver"
}
},
"Labels": {}
}
}
]
Step 2.3: Start a PostgreSQL contaner
Create and run the first PostgreSQL instance with the container name postgresql-app1
:
Important: Make sure you are using the available ports and not conflicting with the existing port. For example, you can use a custom port such as 5433
or 5434
as the host port to avoid any port conflicts.
$ podman run -d \
--name postgresql-app1 \
--secret postgresql_app1_admin_password,type=env,target=POSTGRES_PASSWORD \
-v postgresapp1:/var/lib/pgsql/data:Z \
-p 5433:5432 \
postgres:latest
Here:
5433:5432
maps the container’s PostgreSQL port to the host.postgresapp1
is the container volumes for database storage.POSTGRES_PASSWORD
is the superuser password; mapped from Podman secretpostgresql_app1_admin_password
Step 2.4: Verify the PostgreSQL container
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45d65f183c3e docker.io/library/postgres:latest postgres About a minute ago Up About a minute 0.0.0.0:5433->5432/tcp, 5432/tcp postgresql-app1
Step 3: Generate a Systemd Service File
Generate a systemd service file for the container:
$ mkdir -p ~/.config/systemd/user
$ podman generate systemd --new --name postgresql-app1 > ~/.config/systemd/user/postgresql-app1.service
Step 4: Enable and Start the Service
Enable and start the systemd service for the container under the specific user:
$ systemctl --user daemon-reload
$ systemctl --user enable postgresql-app1.service
$ systemctl --user start postgresql-app1.service
Verify the status:
$ systemctl --user status postgresql-app1.service
Step 6: Testing systemd service – stop and start PostgreSQL Containers
Let us experiment with the systemd service for PostgreSQL – stop and service and check if the PostgreSQL container is running.
$ systemctl --user stop postgresql-app1.service
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Now, start back the systemd service and verify the if PostgreSQL container is running.
$ systemctl --user start postgresql-app1.service
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c8b819776241 docker.io/library/postgres:latest postgres 2 seconds ago Up 3 seconds 0.0.0.0:5433->5432/tcp, 5432/tcp postgresql-app1
Step 7: Verify PostgreSQL Instances
Now our PostgreSQL instance should now be running:
Install the psql
utility coming with the PostgreSQL package. (You can also use alternative utilities)
$ sudo yum install postgresql
Access the PostgreSQL instance: psql -h localhost -p 5432 -U admin -d appdb
$ psql -h localhost -p 5433 -U postgres --password
Password:
psql (13.18, server 17.2 (Debian 17.2-1.pgdg120+1))
WARNING: psql major version 13, server major version 17.
Some psql features might not work.
Type "help" for help.
postgres=#
Step 8: Verify PostgreSQL access from outside of the machine
Ensure the required ports are open and enabled – in this case the custom port 5433.
$ sudo firewall-cmd --list-all
$ sudo firewall-cmd --permanent --add-port=5433/tcp
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-ports
Now, you can repeat the steps with different container names, ports and systemd services to run multiple isolated PostgreSQL instances.
Conclusion
Running PostgreSQL as a container using Podman provides portability, isolation, and ease of management. Integrating with systemd further enhances reliability and automation. By following the steps above, you can efficiently run and manage multiple PostgreSQL instances on the same host, leveraging the robust features of Podman and Red Hat’s trusted container images.