#!/bin/bash ################################################################################ # n8n PostgreSQL Permission Fix Script # # Purpose: Fix PostgreSQL 15+ permission issues for n8n database # Root Cause: PostgreSQL 15+ removed default CREATE permission from PUBLIC # role on the 'public' schema # Solution: Recreate database with proper ownership and explicit grants # # Author: Backend Builder (Claude Code) # Date: 2025-12-01 # Environment: Debian 12, PostgreSQL 16, n8n LXC Container (CT 113) # # SECURITY NOTE: This script requires database password to be set via environment # variable or edited directly before use. ################################################################################ set -e # Exit immediately if a command exits with a non-zero status set -u # Treat unset variables as an error set -o pipefail # Prevent errors in a pipeline from being masked ################################################################################ # CONFIGURATION ################################################################################ DB_NAME="n8n_db" DB_USER="n8n_user" DB_PASSWORD="${N8N_DB_PASSWORD:-YOUR_DB_PASSWORD_HERE}" # Set via env or edit this line DB_HOST="localhost" BACKUP_DIR="/var/backups/n8n" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="${BACKUP_DIR}/n8n_db_backup_${TIMESTAMP}.sql" LOG_FILE="/var/log/n8n_db_fix_${TIMESTAMP}.log" # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color ################################################################################ # FUNCTIONS ################################################################################ log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE" } log_success() { echo -e "${GREEN}[✓]${NC} $1" | tee -a "$LOG_FILE" } log_warning() { echo -e "${YELLOW}[⚠]${NC} $1" | tee -a "$LOG_FILE" } log_error() { echo -e "${RED}[✗]${NC} $1" | tee -a "$LOG_FILE" } check_password() { if [[ "$DB_PASSWORD" == "YOUR_DB_PASSWORD_HERE" ]] || [[ -z "$DB_PASSWORD" ]]; then log_error "Database password not configured!" log_error "Set N8N_DB_PASSWORD environment variable or edit DB_PASSWORD in this script" log_error "Example: export N8N_DB_PASSWORD='your_password_here'" exit 1 fi } check_root() { if [[ $EUID -ne 0 ]]; then log_error "This script must be run as root" exit 1 fi } check_postgresql() { if ! systemctl is-active --quiet postgresql; then log_error "PostgreSQL is not running" exit 1 fi log_success "PostgreSQL service is running" } check_n8n_service() { if systemctl list-unit-files | grep -q "n8n.service"; then return 0 else log_warning "n8n service not found, skipping service management" return 1 fi } stop_n8n() { log "Stopping n8n service..." if check_n8n_service; then systemctl stop n8n || true sleep 3 if systemctl is-active --quiet n8n; then log_error "Failed to stop n8n service" exit 1 fi log_success "n8n service stopped" else log_warning "n8n service not managed by systemd, ensure it's stopped manually" fi } create_backup() { log "Creating backup directory..." mkdir -p "$BACKUP_DIR" chmod 700 "$BACKUP_DIR" log "Creating database backup..." if sudo -u postgres pg_dump -h "$DB_HOST" "$DB_NAME" > "$BACKUP_FILE" 2>/dev/null; then log_success "Database backed up to: $BACKUP_FILE" # Check if backup is empty if [[ ! -s "$BACKUP_FILE" ]]; then log_warning "Backup file is empty (database may be empty)" else BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) log_success "Backup size: $BACKUP_SIZE" fi else log_warning "Database backup failed (database may not exist or be empty)" echo "-- No data to backup" > "$BACKUP_FILE" fi } drop_database() { log "Dropping existing database and recreating with proper ownership..." # Terminate existing connections sudo -u postgres psql <&1 | tee -a "$LOG_FILE" SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$DB_NAME' AND pid <> pg_backend_pid(); EOF # Drop and recreate database sudo -u postgres psql <&1 | tee -a "$LOG_FILE" -- Drop database if exists DROP DATABASE IF EXISTS $DB_NAME; -- Recreate database with n8n_user as owner CREATE DATABASE $DB_NAME OWNER $DB_USER ENCODING 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8' TEMPLATE template0; -- Connect to the database \c $DB_NAME -- Grant all privileges on the public schema to n8n_user GRANT ALL ON SCHEMA public TO $DB_USER; -- Grant all privileges on all tables (current and future) GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO $DB_USER; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO $DB_USER; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO $DB_USER; -- Set default privileges for future objects ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $DB_USER; -- Verify ownership \dt EOF log_success "Database recreated with proper ownership" } test_permissions() { log "Testing database permissions..." # Test connection and DDL operations PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" <&1 | tee -a "$LOG_FILE" -- Test table creation CREATE TABLE IF NOT EXISTS permission_test ( id SERIAL PRIMARY KEY, test_column VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Test insert INSERT INTO permission_test (test_column) VALUES ('Permission test successful'); -- Test select SELECT * FROM permission_test; -- Cleanup test table DROP TABLE permission_test; -- Display current user and database SELECT current_user, current_database(); EOF if [[ $? -eq 0 ]]; then log_success "Permission test PASSED - n8n_user can create tables and perform DDL operations" return 0 else log_error "Permission test FAILED - n8n_user still lacks necessary permissions" return 1 fi } verify_schema_permissions() { log "Verifying schema permissions..." sudo -u postgres psql -d "$DB_NAME" <:5678" echo "" echo "📊 Database Status:" sudo -u postgres psql -d "$DB_NAME" -c "\dt" 2>/dev/null || true echo "" echo "================================================================================" } ################################################################################ # MAIN EXECUTION ################################################################################ main() { echo "================================================================================" echo "n8n PostgreSQL Permission Fix Script" echo "================================================================================" echo "" # Pre-flight checks log "Starting pre-flight checks..." check_root check_password check_postgresql # Execute fix stop_n8n create_backup drop_database verify_schema_permissions test_permissions # Restart and verify start_n8n verify_n8n_startup # Display summary display_summary log_success "Script completed successfully!" } # Execute main function main "$@"