Compare commits

...

5 Commits

  1. 77
      Makefile
  2. 65
      nginx/Makefile
  3. 2
      postgresql/Makefile
  4. 33
      postgresql/config/backup.sh
  5. 8
      postgresql/config/config.env
  6. 37
      postgresql/config/init.sh
  7. 42
      postgresql/postgresql-backup.container
  8. 50
      postgresql/postgresql-init.container
  9. 60
      postgresql/postgresql-server.container
  10. 29
      postgresql/postgresql-set-major.service
  11. 46
      postgresql/postgresql-upgrade.container
  12. 13
      postgresql/postgresql.target

77
Makefile

@ -0,0 +1,77 @@
.PHONY: all install uninstall pre-requisites clean dryrun
PROJECT_NAME := $(shell basename "$${PWD}")
QUADLETS_FILES = $(wildcard *.container *.volume *.network *.pod *.build)
SYSTEMD_FILES = $(wildcard *.service *.target *.timer)
SYSTEMD_UNIT_NAMES := $(wildcard *.service *.target *.timer)
SYSTEMD_MAIN_UNIT_NAMES := $(wildcard *.target)
QUADLET_UNIT_NAMES := $(patsubst %.container, %.service, $(wildcard *.container)) \
$(patsubst %.volume, %-volume.service, $(wildcard *.volume)) \
$(patsubst %.network, %-network.service, $(wildcard *.network)) \
$(patsubst %.pod, %-pod.service, $(wildcard *.pod)) \
$(patsubst %.build, %-build.service, $(wildcard *.build))
CONFIG_FILES = $(wildcard config/*)
TARGET_QUADLETS_FILES = $(addprefix /etc/containers/systemd/, $(QUADLETS_FILES))
TARGET_SYSTEMD_FILES = $(addprefix /etc/systemd/system/, $(SYSTEMD_FILES))
TARGET_CONFIG_FILES = $(patsubst config/%, /etc/quadlets/$(PROJECT_NAME)/%, $(CONFIG_FILES))
pre-requisites:
@test "$$(id -u)" -eq 0 || (echo "This Makefile must be run as root" >&2; exit 1)
all: install
dryrun:
QUADLET_UNIT_DIRS="$$PWD" /usr/lib/systemd/system-generators/podman-system-generator -dryrun > /dev/null
/etc/containers/systemd/%.container: %.container
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.volume: %.volume
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.network: %.network
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.pod: %.pod
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.build: %.build
install -D -m 0644 -o root -g root $< $@
/etc/systemd/system/%.service: %.service
install -D -m 0644 -o root -g root $< $@
/etc/systemd/system/%.target: %.target
install -D -m 0644 -o root -g root $< $@
/etc/systemd/system/%.timer: %.timer
install -D -m 0644 -o root -g root $< $@
/etc/quadlets/$(PROJECT_NAME)/%: config/%
@run() { echo $$*; "$$@"; }; \
if [ -x $< ]; then \
run install -D -m 0755 -o root -g root $< $@; \
else \
run install -D -m 0644 -o root -g root $< $@; \
fi
install: pre-requisites dryrun $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES)
systemctl daemon-reload
systemd-analyze --generators=true verify $(QUADLET_UNIT_NAMES) $(SYSTEMD_UNIT_NAMES)
systemctl enable $(SYSTEMD_UNIT_NAMES)
systemctl start $(SYSTEMD_MAIN_UNIT_NAMES)
uninstall: pre-requisites
systemctl --no-block disable $(SYSTEMD_UNIT_NAMES) || true
systemctl --no-block stop $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES) || true
rm -f $(TARGET_QUADLETS_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES)
systemctl daemon-reload
clean: pre-requisites
@run() { echo $$*; "$$@"; }; \
read -p "This will remove all data of '$(PROJECT_NAME)'. Are you sure? (only 'yes' is accepted) " ans; \
if [ "$$ans" = "yes" ] || [ "$$ans" = "YES" ]; then \
run rm -rf /var/lib/quadlets/$(PROJECT_NAME)/ /var/run/quadlets/$(PROJECT_NAME)/ /etc/quadlets/$(PROJECT_NAME)/; \
else \
echo "Aborted."; exit 1; \
fi

65
nginx/Makefile

@ -1,63 +1,2 @@
.PHONY: all install uninstall pre-requisites clean dryrun PARENT_DIR := ..
include $(PARENT_DIR)/Makefile
PROJECT_NAME := $(shell basename "$${PWD}")
QUADLET_FILES = $(wildcard *.container *.volume *.network *.pod *.build)
SYSTEMD_FILES = $(wildcard *.service *.target *.timer)
SYSTEMD_UNIT_NAMES := $(wildcard *.service *.target *.timer)
SYSTEMD_MAIN_UNIT_NAMES := $(wildcard *.target)
QUADLET_UNIT_NAMES := $(patsubst %.container, %.service, $(wildcard *.container)) \
$(patsubst %.volume, %-volume.service, $(wildcard *.volume)) \
$(patsubst %.network, %-network.service, $(wildcard *.network)) \
$(patsubst %.pod, %-pod.service, $(wildcard *.pod)) \
$(patsubst %.build, %-build.service, $(wildcard *.build))
CONFIG_FILES = $(wildcard config/*)
TARGET_QUADLET_FILES = $(addprefix /etc/containers/systemd/, $(QUADLET_FILES))
TARGET_SYSTEMD_FILES = $(addprefix /etc/systemd/system/, $(SYSTEMD_FILES))
TARGET_CONFIG_FILES = $(patsubst config/%, /etc/quadlets/$(PROJECT_NAME)/%, $(CONFIG_FILES))
pre-requisites:
@test "$$(id -u)" -eq 0 || (echo "This Makefile must be run as root" >&2; exit 1)
all: install
dryrun:
QUADLET_UNIT_DIRS="$$PWD" /usr/lib/systemd/system-generators/podman-system-generator -dryrun > /dev/null
/etc/containers/systemd/%.container: %.container
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.volume: %.volume
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.network: %.network
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.pod: %.pod
install -D -m 0644 -o root -g root $< $@
/etc/containers/systemd/%.build: %.build
install -D -m 0644 -o root -g root $< $@
/etc/systemd/system/%.service: %.service
install -D -m 0644 -o root -g root $< $@
/etc/systemd/system/%.target: %.target
install -D -m 0644 -o root -g root $< $@
/etc/quadlets/$(PROJECT_NAME)/%: config/%
install -D -m 0644 -o root -g root $< $@
install: pre-requisites dryrun $(TARGET_QUADLET_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES)
systemctl daemon-reload
systemd-analyze --generators=true verify $(QUADLET_UNIT_NAMES) $(SYSTEMD_UNIT_NAMES)
systemctl enable $(SYSTEMD_UNIT_NAMES)
systemctl start $(SYSTEMD_MAIN_UNIT_NAMES)
uninstall: pre-requisites
systemctl --no-block disable $(SYSTEMD_UNIT_NAMES)
systemctl --no-block stop $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES)
rm -f $(TARGET_QUADLET_FILES) $(TARGET_SYSTEMD_FILES) $(TARGET_CONFIG_FILES)
systemctl daemon-reload
clean:
rm -rf /var/lib/quadlets/$(PROJECT_NAME)/ /etc/quadlets/$(PROJECT_NAME)/

2
postgresql/Makefile

@ -0,0 +1,2 @@
PARENT_DIR := ..
include $(PARENT_DIR)/Makefile

33
postgresql/config/backup.sh

@ -0,0 +1,33 @@
#!/bin/bash
set -Eeuo pipefail
export PGHOST=/var/run/postgresql
BACKUP_DIR=/var/lib/postgresql/backup/$(date +%Y-%m-%d_%H-%M-%S)/
mkdir -p "$BACKUP_DIR"
echo "Starting complete backup of the whole PostgreSQL server..."
pg_basebackup --pgdata=$BACKUP_DIR --format=tar --manifest-checksums=SHA256 --verbose
echo "Starting backup of individual databases..."
psql -c "SELECT datname FROM pg_database WHERE datname NOT IN ('template0', 'template1', 'postgres');" -t | while read db; do
if [ -z "$db" ]; then
continue
fi
echo "Backup of database $db..."
pg_dump -c --if-exists "$db" | gzip -c > "$BACKUP_DIR/dump-$db.sql.gz"
done
echo "Backup stored in $BACKUP_DIR."
# backup rotation / retention policy
POSTGRES_BACKUP_RETENTION=${POSTGRES_BACKUP_RETENTION:-7}
if [[ "$POSTGRES_BACKUP_RETENTION" -gt 0 ]] && ls -1ct /var/lib/postgresql/backup/*/backup_manifest &>/dev/null; then
echo "Applying backup retention policy: keeping the last $POSTGRES_BACKUP_RETENTION backups."
ls -1ct /var/lib/postgresql/backup/*/backup_manifest | tail -n "+$((POSTGRES_BACKUP_RETENTION + 1))" | while read old_backup; do
old_backup=$(dirname "$old_backup")
echo "Removing old backup: $old_backup"
rm -rf "$old_backup"
done
else
echo "No backup retention policy applied."
fi

8
postgresql/config/config.env

@ -0,0 +1,8 @@
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=postgres
POSTGRES_HOST_AUTH_METHOD=scram-sha-256
POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256
PGPORT=5432
PG_MAJOR=17
POSTGRES_BACKUP_RETENTION=7

37
postgresql/config/init.sh

@ -0,0 +1,37 @@
#!/bin/bash
set -Eeuo pipefail
last_backup=""
for f in /var/lib/postgresql/backup/*/backup_manifest; do
# If there are no backups, the glob pattern above won't match any files
if [ ! -f "$f" ]; then
continue
fi
# Check if this is the most recent backup
if [ -z "$last_backup" ] || [ "$f" -nt "$last_backup" ]; then
last_backup="$f"
fi
done
if [ -n "$last_backup" ]; then
last_backup=$(dirname "$last_backup")
echo "Restoring from last backup: $last_backup..."
mkdir -p "$PGDATA"
tar -xvf "$last_backup/base.tar" -C "$PGDATA"
if [ -f "$last_backup/pg_wal.tar" ]; then
mkdir -p "$PGDATA/pg_wal"
tar -xvf "$last_backup/pg_wal.tar" -C "$PGDATA/pg_wal"
fi
echo "Verifying backup integrity..."
pg_verifybackup -m "$last_backup/backup_manifest" "$PGDATA"
echo "Setting ownership and permissions..."
chown -R postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"
echo "Restoration complete."
exit 0
fi
echo "No previous backup found, initializing an empty database!"
exec /usr/local/bin/docker-ensure-initdb.sh

42
postgresql/postgresql-backup.container

@ -0,0 +1,42 @@
[Unit]
Description=PostgreSQL Database Server - Backup
Documentation=https://hub.docker.com/_/postgres/
After=network.target postgresql-server.service
Requires=postgresql-server.service
# Start/stop this unit when the target is started/stopped
PartOf=postgresql.target
[Container]
ContainerName=postgresql-backup-job
Image=docker.io/library/postgres:${PG_MAJOR}-alpine
# Network configuration
Network=host
# Those environment variables will be injected by podman into the container
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Use a custom backup script
Entrypoint=/usr/local/bin/backup.sh
User=postgres
# Volume mounts
Volume=/var/lib/quadlets/postgresql:/var/lib/postgresql:z
Volume=/etc/quadlets/postgresql/backup.sh:/usr/local/bin/backup.sh:z,ro
# Share /var/run/postgresql/ between containers in the pod for the Unix socket
Volume=/var/run/quadlets/postgresql:/var/run/postgresql:z
[Service]
Restart=no
TimeoutStartSec=600
# These environment variables are sourced to be used by systemd in the Exec* commands
EnvironmentFile=/etc/quadlets/postgresql/config.env
# This container is a job - run once to completion
Type=oneshot
# Skaffold filesystem + fix permissions
ExecStartPre=install -m 0700 -o 70 -g 70 -d /var/lib/quadlets/postgresql/backup

50
postgresql/postgresql-init.container

@ -0,0 +1,50 @@
[Unit]
Description=PostgreSQL Database Server - Initialization
Documentation=https://hub.docker.com/_/postgres/
After=network.target postgresql-set-major.service
Before=postgresql-server.service
Requires=postgresql-set-major.service
# Only start if PostgreSQL has been configured
ConditionPathExists=/etc/quadlets/postgresql/config.env
# and NOT initialized
ConditionPathExists=!/var/lib/quadlets/postgresql/.initialized
# Start/stop this unit when the target is started/stopped
PartOf=postgresql.target
[Container]
ContainerName=postgresql-init-job
Image=docker.io/library/postgres:${PG_MAJOR}-alpine
# Network configuration
Network=host
# PostgreSQL storage is specific to major version
Environment=PGDATA=/var/lib/postgresql/${PG_MAJOR}/docker
# Those environment variables will be injected by podman into the container
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Use the official entrypoint script to initialize the database
Entrypoint=/usr/local/bin/init.sh
# Volume mounts
Volume=/var/lib/quadlets/postgresql:/var/lib/postgresql:z
Volume=/etc/quadlets/postgresql/init.sh:/usr/local/bin/init.sh:z,ro
[Service]
Restart=no
TimeoutStartSec=30
# These environment variables are sourced to be used by systemd in the Exec* commands
EnvironmentFile=/etc/quadlets/postgresql/config.env
# This container is a job - run once to completion
Type=oneshot
# Mark the database as intialized
ExecStartPost=touch /var/lib/quadlets/postgresql/.initialized
[Install]
WantedBy=postgresql.target

60
postgresql/postgresql-server.container

@ -0,0 +1,60 @@
[Unit]
Description=PostgreSQL Database Server
Documentation=https://hub.docker.com/_/postgres/
After=network.target postgresql-upgrade.service postgresql-init.service
Requires=postgresql-upgrade.service postgresql-init.service
Before=postgresql.target
# Only start if PostgreSQL has been configured
ConditionPathExists=/etc/quadlets/postgresql/config.env
# and initialized
ConditionPathExists=/var/lib/quadlets/postgresql/.initialized
# and upgraded
ConditionPathExists=/var/lib/quadlets/postgresql/latest/docker/PG_VERSION
# Start/stop this unit when the target is started/stopped
PartOf=postgresql.target
[Container]
ContainerName=postgresql-server
Image=docker.io/library/postgres:${PG_MAJOR}-alpine
# Network configuration
Network=host
# PostgreSQL storage is specific to major version
Environment=PGDATA=/var/lib/postgresql/${PG_MAJOR}/docker
# Safety flag to avoid initialization of an unwanted database
Environment=DATABASE_ALREADY_EXISTS=true
# Those environment variables will be injected by podman into the container
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Volume mounts
Volume=/var/lib/quadlets/postgresql:/var/lib/postgresql:z
# Health check
HealthCmd=pg_isready -U $POSTGRES_USER -d $POSTGRES_DB -p $PGPORT
HealthInterval=30s
HealthTimeout=10s
HealthStartPeriod=60s
HealthRetries=3
# Share /var/run/postgresql/ between containers in the pod for the Unix socket
Volume=/var/run/quadlets/postgresql:/var/run/postgresql:z
[Service]
Restart=always
RestartSec=10
TimeoutStartSec=120
TimeoutStopSec=30
# These environment variables are sourced to be used by systemd in the Exec* commands
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Skaffold filesystem + fix permissions
ExecStartPre=install -m 0700 -o 70 -g 70 -d /var/run/quadlets/postgresql
[Install]
WantedBy=postgresql.target

29
postgresql/postgresql-set-major.service

@ -0,0 +1,29 @@
[Unit]
Description=PostgreSQL Database Server - Set major version
Documentation=https://hub.docker.com/_/postgres/
Before=postgresql-update.service postgresql-server.service
# Only start if PostgreSQL has been configured
ConditionPathExists=/etc/quadlets/postgresql/config.env
# Start/stop this unit when the target is started/stopped
PartOf=postgresql.target
[Service]
Restart=no
TimeoutStartSec=30
# These environment variables are sourced to be used by systemd in the Exec* commands
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Skaffold filesystem + fix permissions
ExecStartPre=install -m 0700 -o 70 -g 70 -d /var/lib/quadlets/postgresql
# Set the "latest" symlink to point to the desired major version
ExecStart=ln -sfT ${PG_MAJOR} /var/lib/quadlets/postgresql/latest
# This service is a job - run once to completion
Type=oneshot
[Install]
WantedBy=postgresql.target

46
postgresql/postgresql-upgrade.container

@ -0,0 +1,46 @@
[Unit]
Description=PostgreSQL Database Server - Upgrade
Documentation=https://hub.docker.com/r/pgautoupgrade/pgautoupgrade
After=network.target postgresql-set-major.service
Before=postgresql-server.service
Requires=postgresql-set-major.service
# Only start if PostgreSQL has been configured
ConditionPathExists=/etc/quadlets/postgresql/config.env
# and initialized
ConditionPathExists=/var/lib/quadlets/postgresql/.initialized
# and not yet upgraded
ConditionPathExists=!/var/lib/quadlets/postgresql/latest/docker/PG_VERSION
# Start/stop this unit when the target is started/stopped
PartOf=postgresql.target
[Container]
ContainerName=postgresql-upgrade-to-${PG_MAJOR}-job
Image=docker.io/pgautoupgrade/pgautoupgrade:${PG_MAJOR}-alpine
# Network configuration
Network=host
# PostgreSQL storage is specific to major version
Environment=PGDATA=/var/lib/postgresql/${PG_MAJOR}/docker
Environment=PGAUTO_ONESHOT=yes
# Those environment variables will be injected by podman into the container
EnvironmentFile=/etc/quadlets/postgresql/config.env
# Volume mounts
Volume=/var/lib/quadlets/postgresql:/var/lib/postgresql:z
[Service]
Restart=no
TimeoutStartSec=600
# These environment variables are sourced to be used by systemd in the Exec* commands
EnvironmentFile=/etc/quadlets/postgresql/config.env
# This container is a job - run once to completion
Type=oneshot
[Install]
WantedBy=postgresql.target

13
postgresql/postgresql.target

@ -0,0 +1,13 @@
[Unit]
Description=PostgreSQL Service Target
Documentation=man:systemd.target(5)
Requires=postgresql-server.service postgresql-upgrade.service postgresql-init.service postgresql-set-major.service
After=postgresql-server.service postgresql-upgrade.service postgresql-init.service postgresql-set-major.service
# Allow isolation - can stop/start this target independently
AllowIsolate=yes
# Only start if PostgreSQL has been configured
ConditionPathExists=/etc/quadlets/postgresql/config.env
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save