Skip to content

Podman Deployment

Bifolk supports deployment with Podman using Quadlets — systemd unit files that let systemd manage Podman containers natively. This is the recommended production deployment method when using Podman.

Target Audience: System administrators deploying Bifolk with Podman on Linux


Prerequisites

  • Podman 4.4 or higher
  • systemd (standard on most Linux distributions)
  • Git for cloning the repository
  • At least 1 GB of disk space

Enable Lingering

Rootless Podman services run inside your user session. To keep them running after you log out, enable lingering for your user:

loginctl enable-linger $USER

Quadlet Files

Quadlets are located in docker/quadlets/ in the repository:

Variant Directory Database
SQLite docker/quadlets/sqlite/ SQLite (simpler, single-file)
PostgreSQL docker/quadlets/postgres/ PostgreSQL (recommended for production)

Installation

1. Clone the Repository

git clone <repo-url>
cd bifolk

2. Create Podman Secrets

Secrets are stored in Podman's secret store and mounted as read-only files into the container. The entrypoint reads them via the *_FILE environment variables.

SQLite variant (2 secrets):

# Generate and store the Django secret key
python3 -c "import secrets; print(secrets.token_urlsafe(50))" > /tmp/secret_key.txt
podman secret create bifolk_secret_key /tmp/secret_key.txt
rm /tmp/secret_key.txt

# Store the email password (leave empty if email is not configured)
echo "" > /tmp/email_password.txt
podman secret create bifolk_email_password /tmp/email_password.txt
rm /tmp/email_password.txt

PostgreSQL variant (3 secrets):

# Generate and store the Django secret key
python3 -c "import secrets; print(secrets.token_urlsafe(50))" > /tmp/secret_key.txt
podman secret create bifolk_secret_key /tmp/secret_key.txt
rm /tmp/secret_key.txt

# Generate and store the database password
python3 -c "import secrets; print(secrets.token_urlsafe(32))" > /tmp/db_password.txt
podman secret create bifolk_db_password /tmp/db_password.txt
rm /tmp/db_password.txt

# Store the email password (leave empty if email is not configured)
echo "" > /tmp/email_password.txt
podman secret create bifolk_email_password /tmp/email_password.txt
rm /tmp/email_password.txt

3. Create the Environment File

mkdir -p ~/.config/containers/systemd

Copy the example environment file for your chosen variant:

# SQLite
cp docker/quadlets/sqlite/bifolk.env ~/.config/containers/systemd/bifolk.env

# PostgreSQL
cp docker/quadlets/postgres/bifolk.env ~/.config/containers/systemd/bifolk.env

Edit the file and configure your domain, timezone, and email settings:

nano ~/.config/containers/systemd/bifolk.env

Key settings to update:

DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
DJANGO_CSRF_TRUSTED_ORIGINS=https://yourdomain.com
TIME_ZONE=Europe/Berlin

4. Install Quadlet Files

Copy the quadlet files for your chosen variant to the systemd quadlet directory:

# SQLite
cp docker/quadlets/sqlite/*.container \
   docker/quadlets/sqlite/*.network \
   docker/quadlets/sqlite/*.volume \
   ~/.config/containers/systemd/

# PostgreSQL
cp docker/quadlets/postgres/*.container \
   docker/quadlets/postgres/*.network \
   docker/quadlets/postgres/*.volume \
   ~/.config/containers/systemd/

5. Reload systemd and Start Services

systemctl --user daemon-reload

Verify the quadlets were detected:

systemctl --user list-units 'bifolk*'

Start the services:

# SQLite
systemctl --user start bifolk-app.service bifolk-docs.service

# PostgreSQL (start the database first, then the app)
systemctl --user start bifolk-db.service bifolk-app.service bifolk-docs.service

6. Create the Superuser

podman exec -it bifolk-app python /bifolk/app/manage.py createsuperuser

7. Verify

# Check service status
systemctl --user status bifolk-app.service

# Check application health
curl http://localhost:8000/health/

# View recent logs
journalctl --user -u bifolk-app --since "10 minutes ago"

Common Operations

View Logs

journalctl --user -u bifolk-app -f
journalctl --user -u bifolk-db -f

Restart a Service

systemctl --user restart bifolk-app.service

Stop All Services

systemctl --user stop bifolk-app.service bifolk-docs.service

# PostgreSQL only
systemctl --user stop bifolk-db.service

Upgrade Bifolk

# Pull the latest image
podman pull ghcr.io/mwtechnican/bifolk:latest

# Restart to apply the update
systemctl --user restart bifolk-app.service

Rootful Deployment (System-wide)

To run Bifolk as a system-level service (rootful, accessible to all users), copy the quadlets to the system directory and use systemctl without --user:

sudo cp docker/quadlets/sqlite/*.container \
        docker/quadlets/sqlite/*.network \
        docker/quadlets/sqlite/*.volume \
        /etc/containers/systemd/

sudo cp docker/quadlets/sqlite/bifolk.env /etc/containers/systemd/bifolk.env

sudo systemctl daemon-reload
sudo systemctl start bifolk-app.service bifolk-docs.service

Warning

Rootful Podman runs containers as root on the host. Prefer rootless deployment unless your infrastructure specifically requires system-level services.


Troubleshooting

Service Fails to Start

Check the journal for error details:

journalctl --user -u bifolk-app --since "5 minutes ago" -n 50

Quadlets Not Detected After daemon-reload

Verify the files are in the correct directory and have the right extensions:

ls ~/.config/containers/systemd/

Check for syntax errors in the quadlet files:

/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun

Secret Not Found

List available secrets to verify they were created:

podman secret ls

Re-create any missing secrets using the commands in Step 2.

Permission Denied on Volume

The :Z flag on volume mounts applies SELinux relabeling. If you see permission errors unrelated to SELinux, verify that the container starts as root (UID 0) so the entrypoint can run chown before dropping privileges to the bifolk user.


Next Steps