postgres service for external use

This commit is contained in:
berkay 2025-04-19 20:00:57 +03:00
commit 7d1acd8469
17 changed files with 1093 additions and 0 deletions

13
.env.example Normal file
View File

@ -0,0 +1,13 @@
# PostgreSQL Configuration
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres_secure_password
POSTGRES_DB=postgres
# Backup Configuration
BACKUP_DIR=./backups
BACKUP_RETENTION_DAYS=7
# Resource Limits (for container configuration)
# Recommended: 2 CPU cores, 4GB RAM minimum
POSTGRES_CPU_LIMIT=2
POSTGRES_MEMORY_LIMIT=4G

75
Makefile Normal file
View File

@ -0,0 +1,75 @@
# Makefile for PostgreSQL Service
.PHONY: setup start stop restart status logs backup restore monitor clean help
# Default environment
ENV ?= dev
help:
@echo "PostgreSQL Service Management"
@echo "============================"
@echo "Available commands:"
@echo " make setup - Setup the PostgreSQL service (make scripts executable)"
@echo " make start - Start the PostgreSQL service"
@echo " make stop - Stop the PostgreSQL service"
@echo " make restart - Restart the PostgreSQL service"
@echo " make status - Check the status of the PostgreSQL service"
@echo " make logs - View the logs of the PostgreSQL service"
@echo " make backup - Create a backup of the PostgreSQL database"
@echo " make restore - Restore a backup of the PostgreSQL database"
@echo " make monitor - Monitor the PostgreSQL service"
@echo " make clean - Remove all containers, volumes, and networks"
@echo ""
@echo "Environment options:"
@echo " make start ENV=dev - Start with development configuration (default)"
@echo " make start ENV=staging - Start with staging configuration"
@echo " make start ENV=production - Start with production configuration"
setup:
@echo "Setting up PostgreSQL service..."
@cd scripts && ./setup.sh
start:
@echo "Starting PostgreSQL service with $(ENV) environment..."
ifeq ($(ENV), dev)
@docker-compose up -d
else
@docker-compose -f docker-compose.yaml -f environments/$(ENV).yaml up -d
endif
@echo "PostgreSQL service started."
stop:
@echo "Stopping PostgreSQL service..."
ifeq ($(ENV), dev)
@docker-compose down
else
@docker-compose -f docker-compose.yaml -f environments/$(ENV).yaml down
endif
@echo "PostgreSQL service stopped."
restart: stop start
status:
@echo "PostgreSQL service status:"
@docker-compose ps
logs:
@echo "PostgreSQL service logs:"
@docker-compose logs -f postgres
backup:
@echo "Creating PostgreSQL backup..."
@cd scripts && ./backup.sh
restore:
@echo "Restoring PostgreSQL backup..."
@cd scripts && ./restore.sh $(BACKUP)
monitor:
@echo "Monitoring PostgreSQL service..."
@cd scripts && ./monitor.sh
clean:
@echo "Cleaning up PostgreSQL service..."
@docker-compose down -v --remove-orphans
@echo "PostgreSQL service cleaned up."

178
README.md Normal file
View File

@ -0,0 +1,178 @@
# PostgreSQL Production Setup for Proxmox LXC Container
This repository contains a production-ready PostgreSQL setup using Docker Compose, designed to run on a Proxmox LXC container.
## Overview
The configuration includes:
- PostgreSQL 15 with optimized configuration
- Persistent data storage
- Security features
- Health checks
- Resource limits
- Backup and restore capabilities
## Prerequisites
- Proxmox VE with LXC container support
- Docker and Docker Compose installed on the LXC container
- Proper network configuration in Proxmox
## Configuration Details
### docker-compose.yml Explained
```yaml
services:
postgres:
image: postgres:15 # Using PostgreSQL 15
container_name: postgres
restart: always # Ensures PostgreSQL restarts automatically
environment:
# Environment variables for authentication
- POSTGRES_USER=${POSTGRES_USER:-postgres} # Default: postgres
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} # Default: password
- POSTGRES_DB=${POSTGRES_DB:-postgres} # Default: postgres
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
# Persistent data storage
- postgres_data:/var/lib/postgresql/data # Database files
- ./config/postgres.conf:/etc/postgresql/postgresql.conf # Configuration file
- ./init:/docker-entrypoint-initdb.d # Initialization scripts
ports:
- "5432:5432" # Expose PostgreSQL port
command: postgres -c config_file=/etc/postgresql/postgresql.conf
healthcheck:
# Regular health checks
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-postgres}",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- postgres_network
ulimits:
# Increase file descriptor limits for production
nofile:
soft: 64000
hard: 64000
logging:
# Log rotation to prevent disk space issues
driver: "json-file"
options:
max-size: "200m"
max-file: "10"
volumes:
postgres_data: # Persistent volume for database files
driver: local
networks:
postgres_network:
driver: bridge
```
## Security Considerations
1. **Authentication**: PostgreSQL is configured with authentication enabled by default
2. **Environment Variables**: Sensitive information is passed via environment variables
3. **Network Isolation**: Services run on a dedicated bridge network
4. **Configuration**: Optimized PostgreSQL configuration for security and performance
## Initialization Script
The initialization scripts in the `init/` directory:
- Create default roles and permissions
- Set up sample schemas and tables
- Configure database parameters for optimal performance
## Usage
1. Create a `.env` file with your custom credentials:
```
POSTGRES_USER=your_postgres_username
POSTGRES_PASSWORD=your_secure_password
POSTGRES_DB=your_database_name
```
2. Start the services:
```bash
docker-compose up -d
```
3. Connect to PostgreSQL:
```
psql -h your-server-ip -p 5432 -U your_postgres_username -d your_database_name
```
## Backup and Restore
### Creating a Backup
```bash
./scripts/backup.sh
```
### Restoring from Backup
```bash
./scripts/restore.sh <backup-file>
```
## Proxmox LXC Container Configuration
For optimal performance in a Proxmox LXC container:
1. Ensure the container has sufficient resources:
- At least 2 CPU cores
- Minimum 4GB RAM
- At least 20GB storage
2. Enable necessary features in the LXC container:
```
pct set <container-id> -features nesting=1
```
3. Configure container for Docker:
```
echo 'kernel.unprivileged_userns_clone=1' > /etc/sysctl.d/unprivileged-userns-clone.conf
sysctl -p /etc/sysctl.d/unprivileged-userns-clone.conf
```
## Maintenance
- **Backups**: PostgreSQL data is stored in named volumes. Use Docker's volume backup mechanisms:
```bash
docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar -czf /backup/postgres-data-backup.tar.gz /data
```
- **Monitoring**: The service is configured with health checks and can be integrated with monitoring tools like Prometheus and Grafana
- **Updating**: To update PostgreSQL version, change the image tag in docker-compose.yml and restart:
```bash
docker-compose down
# Edit docker-compose.yml to update image version
docker-compose up -d
```
## Troubleshooting
- **Connection Issues**: Ensure ports are not blocked by firewall
- **Performance Issues**: Check PostgreSQL logs with `docker-compose logs postgres`
- **Resource Problems**: Monitor container resource usage and adjust limits if needed
## License
This project is licensed under the MIT License - see the LICENSE file for details.

62
config/postgres-dev.conf Normal file
View File

@ -0,0 +1,62 @@
# PostgreSQL Development Configuration File
# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'
max_connections = 50
password_encryption = scram-sha-256
ssl = off
# RESOURCE USAGE
shared_buffers = 64MB
work_mem = 4MB
maintenance_work_mem = 32MB
effective_cache_size = 1GB
max_worker_processes = 4
max_parallel_workers_per_gather = 1
max_parallel_workers = 4
# WRITE-AHEAD LOG
wal_level = minimal
max_wal_size = 512MB
min_wal_size = 40MB
checkpoint_timeout = 5min
checkpoint_completion_target = 0.9
# QUERY TUNING
random_page_cost = 4.0
effective_io_concurrency = 1
default_statistics_target = 100
# LOGGING
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_rotation = off
log_rotation_age = 1d
log_rotation_size = 10MB
log_min_duration_statement = 250
log_checkpoints = on
log_connections = on
log_disconnections = on
log_duration = on
log_error_verbosity = verbose
log_line_prefix = '%m [%p] %q%u@%d '
log_statement = 'all'
# AUTOVACUUM
autovacuum = on
log_autovacuum_min_duration = 250
autovacuum_max_workers = 2
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
# CLIENT CONNECTION DEFAULTS
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

View File

@ -0,0 +1,75 @@
# PostgreSQL Production Configuration File
# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'
max_connections = 200
password_encryption = scram-sha-256
ssl = on
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
# RESOURCE USAGE
shared_buffers = 2GB
work_mem = 8MB
maintenance_work_mem = 256MB
effective_cache_size = 8GB
max_worker_processes = 12
max_parallel_workers_per_gather = 4
max_parallel_workers = 12
# WRITE-AHEAD LOG
wal_level = replica
max_wal_size = 2GB
min_wal_size = 1GB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'
# REPLICATION
max_wal_senders = 10
wal_keep_size = 1GB
hot_standby = on
hot_standby_feedback = on
# QUERY TUNING
random_page_cost = 1.1
effective_io_concurrency = 200
default_statistics_target = 500
jit = on
# LOGGING
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_rotation = off
log_rotation_age = 1d
log_rotation_size = 100MB
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on
log_duration = off
log_error_verbosity = default
log_line_prefix = '%m [%p] %q%u@%d '
log_statement = 'none'
# AUTOVACUUM
autovacuum = on
log_autovacuum_min_duration = 1000
autovacuum_max_workers = 6
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.025
# CLIENT CONNECTION DEFAULTS
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

View File

@ -0,0 +1,64 @@
# PostgreSQL Staging Configuration File
# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'
max_connections = 100
password_encryption = scram-sha-256
ssl = on
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
# RESOURCE USAGE
shared_buffers = 256MB
work_mem = 6MB
maintenance_work_mem = 64MB
effective_cache_size = 2GB
max_worker_processes = 6
max_parallel_workers_per_gather = 2
max_parallel_workers = 6
# WRITE-AHEAD LOG
wal_level = replica
max_wal_size = 1GB
min_wal_size = 80MB
checkpoint_timeout = 5min
checkpoint_completion_target = 0.9
# QUERY TUNING
random_page_cost = 4.0
effective_io_concurrency = 2
default_statistics_target = 100
# LOGGING
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_rotation = off
log_rotation_age = 1d
log_rotation_size = 10MB
log_min_duration_statement = 500
log_checkpoints = on
log_connections = on
log_disconnections = on
log_duration = off
log_error_verbosity = default
log_line_prefix = '%m [%p] %q%u@%d '
log_statement = 'mod'
# AUTOVACUUM
autovacuum = on
log_autovacuum_min_duration = 500
autovacuum_max_workers = 3
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
# CLIENT CONNECTION DEFAULTS
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

65
config/postgres.conf Normal file
View File

@ -0,0 +1,65 @@
# PostgreSQL Configuration File
# This is the main configuration file that will be used by default
# CONNECTIONS AND AUTHENTICATION
listen_addresses = '*'
max_connections = 100
password_encryption = scram-sha-256
ssl = on
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
# RESOURCE USAGE
shared_buffers = 128MB
work_mem = 4MB
maintenance_work_mem = 64MB
effective_cache_size = 4GB
max_worker_processes = 8
max_parallel_workers_per_gather = 2
max_parallel_workers = 8
# WRITE-AHEAD LOG
wal_level = replica
max_wal_size = 1GB
min_wal_size = 80MB
checkpoint_timeout = 5min
checkpoint_completion_target = 0.9
# QUERY TUNING
random_page_cost = 4.0
effective_io_concurrency = 2
default_statistics_target = 100
# LOGGING
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_rotation = off
log_rotation_age = 1d
log_rotation_size = 10MB
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on
log_duration = off
log_error_verbosity = default
log_line_prefix = '%m [%p] %q%u@%d '
log_statement = 'none'
# AUTOVACUUM
autovacuum = on
log_autovacuum_min_duration = 1000
autovacuum_max_workers = 3
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
# CLIENT CONNECTION DEFAULTS
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

View File

@ -0,0 +1,25 @@
# This override file is for local development and is automatically loaded when running docker-compose up
# without specifying a different file
services:
postgres:
ports:
- "5432:5432"
volumes:
- ./config/postgres-dev.conf:/etc/postgresql/postgresql.conf
environment:
POSTGRES_PASSWORD: postgres_dev_password
command: postgres -c config_file=/etc/postgresql/postgresql.conf
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 5s
timeout: 3s
retries: 3
start_period: 10s
pgadmin:
ports:
- "5050:80"
environment:
PGADMIN_DEFAULT_EMAIL: dev@example.com
PGADMIN_DEFAULT_PASSWORD: pgadmin_dev_password

51
docker-compose.yaml Normal file
View File

@ -0,0 +1,51 @@
services:
postgres:
image: postgres:15 # Using PostgreSQL 15
container_name: postgres
restart: always # Ensures PostgreSQL restarts automatically
environment:
# Environment variables for authentication
- POSTGRES_USER=${POSTGRES_USER:-postgres} # Default: postgres
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} # Default: password
- POSTGRES_DB=${POSTGRES_DB:-postgres} # Default: postgres
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
# Persistent data storage
- postgres_data:/var/lib/postgresql/data # Database files
- ./config/postgres.conf:/etc/postgresql/postgresql.conf # Configuration file
- ./init:/docker-entrypoint-initdb.d # Initialization scripts
ports:
- "5432:5432" # Expose PostgreSQL port
command: postgres -c config_file=/etc/postgresql/postgresql.conf
healthcheck:
# Regular health checks
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-postgres}",
]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- postgres_network
ulimits:
# Increase file descriptor limits for production
nofile:
soft: 64000
hard: 64000
logging:
# Log rotation to prevent disk space issues
driver: "json-file"
options:
max-size: "200m"
max-file: "10"
volumes:
postgres_data: # Persistent volume for database files
driver: local
networks:
postgres_network:
driver: bridge

33
environments/dev.yaml Normal file
View File

@ -0,0 +1,33 @@
version: '3.8'
services:
postgres:
environment:
POSTGRES_USER: postgres_dev
POSTGRES_PASSWORD: postgres_dev_password
POSTGRES_DB: postgres_dev
ports:
- "5432:5432"
volumes:
- postgres_data_dev:/var/lib/postgresql/data
- ./config/postgres-dev.conf:/etc/postgresql/postgresql.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf
pgadmin:
environment:
PGADMIN_DEFAULT_EMAIL: dev@example.com
PGADMIN_DEFAULT_PASSWORD: pgadmin_dev_password
ports:
- "5050:80"
volumes:
- pgadmin_data_dev:/var/lib/pgadmin
volumes:
postgres_data_dev:
name: postgres_data_dev
pgadmin_data_dev:
name: pgadmin_data_dev
networks:
postgres_network:
name: postgres_network_dev

View File

@ -0,0 +1,82 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "5432:5432"
volumes:
- postgres_data_prod:/var/lib/postgresql/data
- ./config/postgres-production.conf:/etc/postgresql/postgresql.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf
deploy:
resources:
limits:
cpus: '2'
memory: 4G
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "200m"
max-file: "10"
pgadmin:
image: dpage/pgadmin4:latest
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
PGADMIN_CONFIG_SERVER_MODE: 'True'
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'True'
ports:
- "5050:80"
volumes:
- pgadmin_data_prod:/var/lib/pgadmin
deploy:
resources:
limits:
cpus: '1'
memory: 1G
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
volumes:
postgres_data_prod:
name: postgres_data_prod
driver: local
driver_opts:
type: 'none'
o: 'bind'
device: '/data/postgres'
pgadmin_data_prod:
name: pgadmin_data_prod
driver: local
driver_opts:
type: 'none'
o: 'bind'
device: '/data/pgadmin'
networks:
postgres_network:
name: postgres_network_prod
driver: bridge
ipam:
driver: default
config:
- subnet: 172.28.0.0/16

43
environments/staging.yaml Normal file
View File

@ -0,0 +1,43 @@
version: '3.8'
services:
postgres:
environment:
POSTGRES_USER: postgres_staging
POSTGRES_PASSWORD: postgres_staging_password
POSTGRES_DB: postgres_staging
ports:
- "5433:5432"
volumes:
- postgres_data_staging:/var/lib/postgresql/data
- ./config/postgres-staging.conf:/etc/postgresql/postgresql.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf
deploy:
resources:
limits:
cpus: '1'
memory: 1G
pgadmin:
environment:
PGADMIN_DEFAULT_EMAIL: staging@example.com
PGADMIN_DEFAULT_PASSWORD: pgadmin_staging_password
ports:
- "5051:80"
volumes:
- pgadmin_data_staging:/var/lib/pgadmin
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
volumes:
postgres_data_staging:
name: postgres_data_staging
pgadmin_data_staging:
name: pgadmin_data_staging
networks:
postgres_network:
name: postgres_network_staging

84
init/01-init.sql Normal file
View File

@ -0,0 +1,84 @@
-- PostgreSQL Initialization Script
-- This script will be executed when the PostgreSQL container is first created
-- Create extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- Create roles
CREATE ROLE readonly;
GRANT CONNECT ON DATABASE postgres TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
CREATE ROLE readwrite;
GRANT CONNECT ON DATABASE postgres TO readwrite;
GRANT USAGE, CREATE ON SCHEMA public TO readwrite;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO readwrite;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO readwrite;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO readwrite;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO readwrite;
-- Create sample users
CREATE USER sample_readonly WITH PASSWORD 'readonly_password';
GRANT readonly TO sample_readonly;
CREATE USER sample_readwrite WITH PASSWORD 'readwrite_password';
GRANT readwrite TO sample_readwrite;
-- Create sample schema and tables
CREATE SCHEMA IF NOT EXISTS sample;
CREATE TABLE IF NOT EXISTS sample.users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(100) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS sample.posts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES sample.users(id) ON DELETE CASCADE,
title VARCHAR(200) NOT NULL,
content TEXT,
is_published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_users_username ON sample.users(username);
CREATE INDEX IF NOT EXISTS idx_users_email ON sample.users(email);
CREATE INDEX IF NOT EXISTS idx_posts_user_id ON sample.posts(user_id);
CREATE INDEX IF NOT EXISTS idx_posts_created_at ON sample.posts(created_at);
-- Insert sample data
INSERT INTO sample.users (username, email, password_hash, first_name, last_name)
VALUES
('johndoe', 'john.doe@example.com', crypt('password123', gen_salt('bf')), 'John', 'Doe'),
('janedoe', 'jane.doe@example.com', crypt('password123', gen_salt('bf')), 'Jane', 'Doe')
ON CONFLICT (username) DO NOTHING;
-- Get user IDs for sample posts
DO $$
DECLARE
john_id UUID;
jane_id UUID;
BEGIN
SELECT id INTO john_id FROM sample.users WHERE username = 'johndoe';
SELECT id INTO jane_id FROM sample.users WHERE username = 'janedoe';
INSERT INTO sample.posts (user_id, title, content, is_published)
VALUES
(john_id, 'First Post', 'This is my first post content.', TRUE),
(john_id, 'Second Post', 'This is my second post content.', FALSE),
(jane_id, 'Hello World', 'Hello world post content.', TRUE)
ON CONFLICT DO NOTHING;
END $$;

60
scripts/backup.sh Normal file
View File

@ -0,0 +1,60 @@
#!/bin/bash
# PostgreSQL Backup Script for Proxmox LXC Container
# Load environment variables
if [ -f ../.env ]; then
source ../.env
else
echo "Error: .env file not found!"
exit 1
fi
# Set default values if not provided in .env
BACKUP_DIR=${BACKUP_DIR:-"../backups"}
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
BACKUP_RETENTION_DAYS=${BACKUP_RETENTION_DAYS:-7}
# Create backup directory if it doesn't exist
mkdir -p ${BACKUP_DIR}
# Generate timestamp for backup file
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="${BACKUP_DIR}/postgres_backup_${TIMESTAMP}.sql.gz"
echo "Starting PostgreSQL backup..."
echo "Database: ${POSTGRES_DB}"
echo "Backup file: ${BACKUP_FILE}"
# Perform the backup
docker exec -t postgres pg_dump -U ${POSTGRES_USER} -d ${POSTGRES_DB} | gzip > ${BACKUP_FILE}
# Check if backup was successful
if [ $? -eq 0 ]; then
echo "Backup completed successfully: ${BACKUP_FILE}"
# Remove backups older than retention period
find ${BACKUP_DIR} -name "postgres_backup_*.sql.gz" -type f -mtime +${BACKUP_RETENTION_DAYS} -delete
echo "Cleaned up old backups (older than ${BACKUP_RETENTION_DAYS} days)"
else
echo "Error: Backup failed!"
exit 1
fi
# Print backup information
echo "Backup size: $(du -h ${BACKUP_FILE} | cut -f1)"
echo "Available backups:"
ls -lh ${BACKUP_DIR}
# Create a full volume backup as well (for disaster recovery)
echo "Creating volume backup..."
VOLUME_BACKUP_FILE="${BACKUP_DIR}/postgres_volume_backup_${TIMESTAMP}.tar.gz"
docker run --rm -v postgres_data:/data -v $(pwd)/${BACKUP_DIR}:/backup alpine tar -czf /backup/$(basename ${VOLUME_BACKUP_FILE}) /data
if [ $? -eq 0 ]; then
echo "Volume backup completed successfully: ${VOLUME_BACKUP_FILE}"
else
echo "Warning: Volume backup failed!"
fi
exit 0

68
scripts/monitor.sh Normal file
View File

@ -0,0 +1,68 @@
#!/bin/bash
# PostgreSQL Monitoring Script for Proxmox LXC Container
# Load environment variables
if [ -f ../.env ]; then
source ../.env
else
echo "Error: .env file not found!"
exit 1
fi
# Set default values if not provided in .env
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
echo "PostgreSQL Service Monitoring for Proxmox LXC"
echo "=========================================="
echo
# Check container status
echo "Container Status:"
docker ps --filter "name=postgres" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo
# Check PostgreSQL service health
echo "PostgreSQL Health Check:"
docker exec postgres pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
if [ $? -eq 0 ]; then
echo "PostgreSQL is accepting connections"
else
echo "PostgreSQL is not accepting connections"
fi
echo
# Database size information
echo "Database Size Information:"
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) as size FROM pg_database ORDER BY pg_database_size(datname) DESC;"
echo
# Connection information
echo "Current Connections:"
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT datname, count(*) as connections FROM pg_stat_activity GROUP BY datname;"
echo
# Active queries
echo "Active Queries (running for more than 5 seconds):"
docker exec postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB} -c "SELECT pid, datname, usename, application_name, client_addr, state, now() - query_start as duration, query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 seconds';"
echo
# Resource usage
echo "Container Resource Usage:"
docker stats postgres --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
echo
# Disk usage
echo "Disk Usage:"
df -h $(docker volume inspect -f '{{ .Mountpoint }}' postgres_data)
echo
# System resource usage
echo "System Resource Usage:"
echo "CPU:"
top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1"%"}'
echo "Memory:"
free -m | awk 'NR==2{printf "Used: %s MB (%.2f%%)", $3, $3*100/$2 }'
echo
exit 0

94
scripts/restore.sh Normal file
View File

@ -0,0 +1,94 @@
#!/bin/bash
# PostgreSQL Restore Script for Proxmox LXC Container
# Check if backup file is provided
if [ $# -ne 1 ]; then
echo "Usage: $0 <backup-file>"
echo "Example: $0 ../backups/postgres_backup_20250419_120000.sql.gz"
echo " $0 ../backups/postgres_volume_backup_20250419_120000.tar.gz"
exit 1
fi
BACKUP_FILE=$1
# Check if backup file exists
if [ ! -f "${BACKUP_FILE}" ]; then
echo "Error: Backup file '${BACKUP_FILE}' not found!"
exit 1
fi
# Load environment variables
if [ -f ../.env ]; then
source ../.env
else
echo "Error: .env file not found!"
exit 1
fi
# Set default values if not provided in .env
POSTGRES_USER=${POSTGRES_USER:-"postgres"}
POSTGRES_DB=${POSTGRES_DB:-"postgres"}
echo "Starting PostgreSQL restore..."
echo "Database: ${POSTGRES_DB}"
echo "Backup file: ${BACKUP_FILE}"
# Determine backup type (SQL dump or volume backup)
if [[ "${BACKUP_FILE}" == *"postgres_backup_"* ]]; then
# SQL dump restore
# Confirm restore operation
read -p "Warning: This will overwrite the existing database. Continue? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Restore operation cancelled."
exit 0
fi
# Perform the restore
echo "Restoring database from SQL dump..."
gunzip -c ${BACKUP_FILE} | docker exec -i postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}
# Check if restore was successful
if [ $? -eq 0 ]; then
echo "Restore completed successfully from: ${BACKUP_FILE}"
else
echo "Error: Restore failed!"
exit 1
fi
elif [[ "${BACKUP_FILE}" == *"postgres_volume_backup_"* ]]; then
# Volume backup restore
# Confirm restore operation
read -p "WARNING: This will completely replace the PostgreSQL data volume. All current data will be lost. Continue? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Restore operation cancelled."
exit 0
fi
echo "Stopping PostgreSQL container..."
docker-compose down
echo "Restoring volume data from backup..."
# Create a temporary container to restore the volume
docker run --rm -v postgres_data:/data -v $(pwd)/$(dirname ${BACKUP_FILE}):/backup alpine sh -c "rm -rf /data/* && tar -xzf /backup/$(basename ${BACKUP_FILE}) -C / --strip-components=1"
# Check if restore was successful
if [ $? -eq 0 ]; then
echo "Volume restore completed successfully from: ${BACKUP_FILE}"
echo "Starting PostgreSQL container..."
docker-compose up -d
echo "Waiting for PostgreSQL to start..."
sleep 10
echo "PostgreSQL service restored and running."
else
echo "Error: Volume restore failed!"
echo "Starting PostgreSQL container..."
docker-compose up -d
exit 1
fi
else
echo "Error: Unknown backup file format. Expected postgres_backup_*.sql.gz or postgres_volume_backup_*.tar.gz"
exit 1
fi
exit 0

21
scripts/setup.sh Normal file
View File

@ -0,0 +1,21 @@
#!/bin/bash
# Setup script for PostgreSQL service
# Make scripts executable
chmod +x ./backup.sh
chmod +x ./restore.sh
chmod +x ./monitor.sh
# Create necessary directories
mkdir -p ../backups
mkdir -p ../logs
# Copy environment file if it doesn't exist
if [ ! -f ../.env ]; then
cp ../.env.example ../.env
echo "Created .env file from .env.example"
echo "Please update the .env file with your configuration"
fi
echo "Setup completed successfully!"
echo "You can now start the PostgreSQL service with: docker-compose up -d"