commit c2dadf6148efd6fa6f9674a11b94061a7cdaf768 Author: Tour Date: Tue Dec 9 11:12:08 2025 +0100 first commit diff --git a/.env b/.env new file mode 100644 index 0000000..57bf95b --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +POSTGRES_PASSWORD=heel-goed-wachtwoord +PGADMIN_PASSWORD=heel-goed-wachtwoord \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef076be --- /dev/null +++ b/README.md @@ -0,0 +1,188 @@ +# PostgreSQL Infrastructure Setup + +This folder contains the Docker-based PostgreSQL infrastructure running on the internal server (`192.168.1.159`). +It provides a production-ready PostgreSQL instance, optional pgAdmin UI, and a simple automated backup mechanism. + +The setup is designed to be: + +- persistent (volume-backed database storage) +- LAN-accessible (port `5432`) +- isolated from Traefik (database does not use reverse proxy) +- easy to operate (`docker compose up -d`) +- safe (controlled permissions and separate admin password) + +--- + +## Directory Structure + +``` +infra/postgres/ + docker-compose.yml → main PostgreSQL service + .env → database credentials (not committed) + data/ → PostgreSQL data directory (automatically created) + backup/ → SQL dumps created by backup containers + pgadmin/ → optional pgAdmin admin UI exposed via Traefik + backup/ → backup runner files (optional) +``` + +`data/` and `backup/` are *not* included in version control. +They are created on the server when the stack is deployed. + +--- + +## Prerequisites + +Run on the target server (`Tour`, 192.168.1.159): + +- Docker +- Docker Compose v2 +- Access to the `traefik-net` Docker network (only if pgAdmin is used) + +--- + +## Environment File + +Create `.env` inside this directory: + +``` +POSTGRES_PASSWORD= +PGADMIN_PASSWORD= +``` + +Recommended permissions: + +```bash +chmod 600 .env +``` + +--- + +## Initial Setup + +Run these commands on the server: + +```bash +cd ~/infra/postgres +mkdir -p data backup +``` + +Ensure correct directory ownership: + +```bash +sudo chown -R $USER:$USER data backup +``` + +--- + +## Starting the Stack + +Start PostgreSQL: + +```bash +docker compose up -d +``` + +Check status: + +```bash +docker ps +``` + +Expected: + +``` +postgres-db Up (healthy) 0.0.0.0:5432->5432/tcp +``` + +--- + +## Connecting to PostgreSQL + +Connection string: + +``` +postgresql://auction:@192.168.1.159:5432/auctiondb +``` + +Test: + +```bash +psql "postgresql://auction:@192.168.1.159:5432/auctiondb" +``` + +--- + +## pgAdmin (Optional) + +If pgAdmin is deployed via Traefik, access it at: + +``` +https://pgadmin.appmodel.nl/ +``` + +Use `postgres-db` as the hostname inside pgAdmin. + +--- + +## Backup Strategy + +SQL dumps are stored in: + +``` +infra/postgres/backup/ +``` + +Daily backups produce files like: + +``` +backup_YYYY-MM-DD_HH-MM.sql +``` + +Restore example: + +```bash +psql -U auction -d auctiondb < backup_file.sql +``` + +--- + +## Stopping or Updating + +Stop: + +```bash +docker compose down +``` + +Update: + +```bash +docker compose pull +docker compose up -d +``` + +--- + +## Disaster Recovery + +To rebuild the database: + +```bash +docker compose down +mv data data_old_$(date +%s) +docker compose up -d +psql -U auction -d auctiondb < backup/latest.sql +``` + +--- + +## Next Steps + +Possible extensions: + +- Flyway migration container +- Auto schema sync for Python + Quarkus +- Monitoring exporters +- Retention policy for backups +- Migration from SQLite to PostgreSQL + diff --git a/backup/backup.sh b/backup/backup.sh new file mode 100644 index 0000000..8373fc1 --- /dev/null +++ b/backup/backup.sh @@ -0,0 +1,14 @@ +#!/bin/bash +DATE=$(date +"%Y-%m-%d_%H-%M") +FILENAME="backup_$DATE.sql" + +echo "Dumping database to $FILENAME" + +/usr/bin/pg_dump \ +-h postgres-db \ +-U auction \ +auctiondb \ +> /backup/$FILENAME + + # optional: keep only 14 days +find /backup -type f -mtime +14 -delete diff --git a/backup/docker-compose.yml b/backup/docker-compose.yml new file mode 100644 index 0000000..75fdc63 --- /dev/null +++ b/backup/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3.9" + +services: + pgbackup: + image: postgres:16 + container_name: postgres-backup + restart: unless-stopped + + environment: + PGPASSWORD: ${POSTGRES_PASSWORD} + + volumes: + - ../postgres/backup:/backup + - ./backup.sh:/backup.sh + + entrypoint: [ "bash", "-c", "echo 'Starting backup scheduler'; while true; do /backup.sh; sleep 86400; done" ] + + networks: + - default diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f9cd324 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: "3.9" + +services: + postgres: + image: postgres:16 + container_name: postgres-db + restart: unless-stopped + + environment: + POSTGRES_DB: auctiondb + POSTGRES_USER: auction + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + + ports: + - "5432:5432" # LAN only, safe inside 192.168.1.x + + volumes: + - ./data:/var/lib/postgresql/data + - ./backup:/backup + + healthcheck: + test: ["CMD-SHELL", "pg_isready -U auction -d auctiondb"] + interval: 5s + timeout: 2s + retries: 10 diff --git a/install_postgres.sh b/install_postgres.sh new file mode 100644 index 0000000..22c0c28 --- /dev/null +++ b/install_postgres.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +set -e + +echo "=== PostgreSQL Infra Installer ===" + +# Detect working directory (should be infra/postgres) +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$SCRIPT_DIR" + +echo "Using infra directory: $SCRIPT_DIR" + +# ----------------------------------------------------------- +# 1. Ensure directories exist +# ----------------------------------------------------------- +echo "→ Creating required directories..." +mkdir -p data +mkdir -p backup + +# ----------------------------------------------------------- +# 2. Permissions +# ----------------------------------------------------------- +echo "→ Setting directory permissions..." +sudo chown -R "$USER:$USER" data backup + +# ----------------------------------------------------------- +# 3. Create .env if missing +# ----------------------------------------------------------- +if [ ! -f ".env" ]; then + echo "→ Creating .env file..." + cat < .env +POSTGRES_PASSWORD=$(openssl rand -hex 16) +PGADMIN_PASSWORD=$(openssl rand -hex 16) +EOF + echo "Generated random passwords in .env" +else + echo "→ .env file already exists; skipping creation." +fi + +echo "Current .env:" +cat .env +echo "" + +# ----------------------------------------------------------- +# 4. Verify Docker & Docker Compose +# ----------------------------------------------------------- +echo "→ Checking Docker installation..." +if ! command -v docker &> /dev/null; then + echo "ERROR: Docker is not installed." + exit 1 +fi + +echo "→ Checking Docker Compose..." +if ! docker compose version &> /dev/null; then + echo "ERROR: Docker Compose V2 is not installed." + exit 1 +fi + +# ----------------------------------------------------------- +# 5. Start PostgreSQL stack +# ----------------------------------------------------------- +echo "→ Starting PostgreSQL Docker stack..." +docker compose up -d + +echo "→ Waiting for PostgreSQL to report healthy status..." +sleep 3 +docker ps --filter "name=postgres-db" + +# ----------------------------------------------------------- +# 6. Setup backup container +# ----------------------------------------------------------- +echo "→ Checking backup service..." + +if [ -d "backup" ] && [ -f "backup/docker-compose.yml" ]; then + echo "→ Starting backup runner..." + cd backup + docker compose up -d + cd "$SCRIPT_DIR" +else + echo "⚠ Backup runner folder missing. Expected: ./backup/docker-compose.yml" + echo "Skipping backup setup." +fi + +# ----------------------------------------------------------- +# 7. Summary +# ----------------------------------------------------------- +echo "" +echo "====================================================" +echo " PostgreSQL Infra Installed Successfully" +echo "====================================================" +echo "Host machine: $(hostname)" +echo "Database port: 5432 (LAN accessible)" +echo "Data dir: $SCRIPT_DIR/data" +echo "Backups: $SCRIPT_DIR/backup/" +echo "" +echo "Connection string:" +echo " postgresql://auction:@192.168.1.159:5432/auctiondb" +echo "" +echo "Start/Stop commands:" +echo " docker compose up -d" +echo " docker compose down" +echo "" +echo "If pgAdmin is enabled, visit:" +echo " https://pgadmin.appmodel.nl/" +echo "" +echo "Done." diff --git a/pgadmin/docker-compose.yml b/pgadmin/docker-compose.yml new file mode 100644 index 0000000..5714f70 --- /dev/null +++ b/pgadmin/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.9" + +services: + pgadmin: + image: dpage/pgadmin4:8 + container_name: pgadmin + restart: unless-stopped + + environment: + PGADMIN_DEFAULT_EMAIL: admin@appmodel.nl + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD} + PGADMIN_CONFIG_SERVER_MODE: "True" + + volumes: + - ./data:/var/lib/pgadmin + + networks: + - traefik-net + - default + + labels: + - "traefik.enable=true" + - "traefik.http.routers.pgadmin.rule=Host(`pgadmin.appmodel.nl`)" + - "traefik.http.routers.pgadmin.entrypoints=websecure" + - "traefik.http.routers.pgadmin.tls.certresolver=letsencrypt" + +networks: + traefik-net: + external: true