#!/bin/bash # Must be run with root privileges # Error handling set -e trap 'echo "Error on line $LINENO. Exit code: $?"' ERR # Configuration INSTALL_DIR="/docker/paperless-ngx" COMPOSE_FILE="${INSTALL_DIR}/docker-compose.yml" LOG_FILE="/var/log/paperless-install.log" # Utility functions log_message() { local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1" echo "$message" echo "$message" >> "$LOG_FILE" } show_container_status() { log_message "Current container statuses:" echo "----------------------------------------" docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" echo "----------------------------------------" } check_service_status() { local service=$1 local container_name="paperless-ngx-${service}-1" # Fixed naming convention if ! docker ps -q -f name="$container_name" >/dev/null 2>&1; then return 1 fi local status=$(docker inspect -f '{{.State.Status}}' "$container_name" 2>/dev/null) if [ "$status" != "running" ]; then return 1 fi # Only perform additional health checks for specific services case "$service" in "webserver") if ! curl -sf http://localhost:8000 >/dev/null 2>&1; then return 1 fi ;; "db") # Optional: Add PostgreSQL specific check if needed docker exec "$container_name" pg_isready -U paperless >/dev/null 2>&1 || return 1 ;; "broker") # Redis broker only needs container running status check return 0 ;; esac return 0 } verify_all_services() { local services=("broker" "db" "webserver") local failed_services=() for service in "${services[@]}"; do case "$service" in "broker") # Just check if Redis is running, no wait needed if ! check_service_status "$service"; then failed_services+=("$service") fi ;; *) log_message "Checking $service service..." if ! check_service_status "$service"; then failed_services+=("$service") fi ;; esac done if [ ${#failed_services[@]} -ne 0 ]; then log_message "The following services are not running properly: ${failed_services[*]}" return 1 fi log_message "All services are running properly" return 0 } check_docker_version() { local current_version=$(docker --version | cut -d ' ' -f3 | cut -d ',' -f1) log_message "Current Docker version: $current_version" # Check for updates apt-get update >/dev/null 2>&1 local available_version=$(apt-cache policy docker-ce | grep Candidate | cut -d ' ' -f4) if [ "$current_version" != "$available_version" ]; then log_message "New Docker version available: $available_version" read -p "Do you want to upgrade Docker? (y/N) " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then apt-get install -y docker-ce docker-ce-cli containerd.io fi fi } redeploy_service() { local service=$1 log_message "Redeploying $service..." cd "$INSTALL_DIR" docker compose stop "$service" docker compose rm -f "$service" docker compose up -d "$service" case "$service" in "broker") # Redis broker needs no health wait log_message "Redis broker redeployed" return 0 ;; "db") # Wait for database to be ready local retries=0 while ! check_service_status "$service" && [ $retries -lt 5 ]; do log_message "Waiting for database to be ready..." sleep 5 retries=$((retries + 1)) done ;; "webserver") # Wait for webserver to be healthy local retries=0 while ! check_service_status "$service" && [ $retries -lt 5 ]; do log_message "Waiting for webserver to become healthy..." sleep 10 retries=$((retries + 1)) done ;; esac if [ $retries -eq 5 ]; then log_message "Failed to redeploy $service after 5 attempts" return 1 fi log_message "$service redeployed successfully" return 0 } backup_existing_data() { if [ -d "$INSTALL_DIR" ]; then local backup_dir="${INSTALL_DIR}_backup_$(date +%Y%m%d_%H%M%S)" log_message "Creating backup of existing installation at $backup_dir" cp -r "$INSTALL_DIR" "$backup_dir" fi } check_existing_installation() { log_message "Checking existing Paperless-ngx installation..." if [ -f "$COMPOSE_FILE" ]; then log_message "Existing installation found" show_container_status if verify_all_services; then log_message "? All services are running correctly" log_message "? Paperless-ngx is accessible at http://localhost:8000" log_message "? Redis and PostgreSQL containers are properly isolated" return 0 else log_message "? Service issues detected. Starting recovery process..." return 1 fi fi return 1 } # Main script execution starts here # Check if script is run as root if [ "$EUID" -ne 0 ]; then echo "Please run as root" exit 1 fi # Initialize log file mkdir -p "$(dirname "$LOG_FILE")" touch "$LOG_FILE" log_message "Starting Paperless-ngx installation/verification script" # Check if Docker is installed if ! command -v docker &> /dev/null; then log_message "Docker not found. Installing Docker..." curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh apt-get update apt-get install -y docker-compose-plugin log_message "Docker installed successfully" else log_message "Docker already installed" check_docker_version fi # Check existing installation or proceed with new installation if check_existing_installation; then exit 0 else if [ -f "$COMPOSE_FILE" ]; then # Attempt to recover existing installation backup_existing_data # Attempt to redeploy failed services for service in broker db webserver; do if ! check_service_status "$service"; then if ! redeploy_service "$service"; then log_message "Failed to redeploy $service. Manual intervention may be required." exit 1 fi fi done else # New installation log_message "Starting new Paperless-ngx installation" # Create installation directory mkdir -p "$INSTALL_DIR" cd "$INSTALL_DIR" # Create docker-compose.yml cat > docker-compose.yml <<'EOF' version: "3.4" services: broker: image: redis:7 restart: unless-stopped networks: - paperless_internal expose: - 6379 volumes: - redisdata:/data db: image: postgres:15 restart: unless-stopped networks: - paperless_internal expose: - 5432 volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_DB: paperless POSTGRES_USER: paperless POSTGRES_PASSWORD: paperless webserver: image: ghcr.io/paperless-ngx/paperless-ngx:latest restart: unless-stopped networks: - paperless_internal - paperless_external depends_on: - db - broker ports: - "8000:8000" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000"] interval: 30s timeout: 10s retries: 5 volumes: - data:/usr/src/paperless/data - media:/usr/src/paperless/media - ./export:/usr/src/paperless/export - ./consume:/usr/src/paperless/consume environment: PAPERLESS_REDIS: redis://broker:6379 PAPERLESS_DBHOST: db PAPERLESS_DB_USER: paperless PAPERLESS_DB_PASSWORD: paperless PAPERLESS_DB_NAME: paperless PAPERLESS_TIME_ZONE: Europe/London PAPERLESS_OCR_LANGUAGE: eng PAPERLESS_SECRET_KEY: change-this-key PAPERLESS_URL: https://your-domain.com networks: paperless_internal: internal: true # Only accessible within Docker paperless_external: # For external access to webserver volumes: data: name: paperless_data media: name: paperless_media pgdata: name: paperless_pgdata redisdata: name: paperless_redisdata EOF # Create required directories and set permissions mkdir -p export consume chown -R 1000:1000 export consume # Start services log_message "Starting Paperless-ngx services..." docker compose up -d # Give services more time to initialize on first deployment log_message "Waiting for services to initialize (this may take a minute)..." sleep 30 # Initial wait for containers to start # Additional wait loop with status checks for i in {1..6}; do # Try for up to 1 minute more if docker compose ps | grep -q "healthy"; then break # Exit loop if containers are healthy fi log_message "Still waiting for services to be ready... (attempt $i/6)" sleep 10 done if verify_all_services; then log_message "? Installation completed successfully!" show_container_status # Create superuser log_message "Creating superuser account..." if docker compose run --rm webserver createsuperuser; then log_message "? Superuser created successfully" log_message "? You can access Paperless-ngx at http://localhost:8000" else log_message "? Failed to create superuser. You can create one later using:" log_message " docker compose run --rm webserver createsuperuser" log_message "? You can access Paperless-ngx at http://localhost:8000" fi log_message "Important next steps:" log_message "1. Change the PAPERLESS_URL in docker-compose.yml to your domain" log_message "2. Change the PAPERLESS_SECRET_KEY to a random string" log_message "3. Set up a reverse proxy in CloudPanel" log_message "4. Create SSL certificate for your domain" else log_message "Installation completed but some services are not running properly" log_message "Please check the logs and try redeploying the failed services" exit 1 fi fi fi