#!/bin/bash # Exit on any error set -e echo "=====================================" echo " System Dependency Check " echo "=====================================" # Function to install Docker if missing install_docker() { echo "Docker not found. Installing Docker and Docker Compose..." # Update package database sudo apt-get update -y # Install prerequisites sudo apt-get install -y ca-certificates curl gnupg lsb-release # Add Docker’s official GPG key sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg --yes # Set up the repository echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.p/docker.list > /dev/null # Install Docker Engine and the Compose plugin sudo apt-get update -y sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Start and enable Docker service sudo systemctl start docker sudo systemctl enable docker echo "Docker and Docker Compose installed successfully!" } # Check for Docker core engine if ! command -v docker &> /dev/null; then install_docker else echo "-> Docker engine is already installed." fi # Check for Docker Compose V2 plugin (docker compose) if ! docker compose version &> /dev/null; then echo "Docker Compose V2 plugin missing. Attempting installation..." sudo apt-get update -y && sudo apt-get install -y docker-compose-plugin else echo "-> Docker Compose V2 plugin is already installed." fi echo "=====================================" echo " Stack Configuration Script " echo "=====================================" # 1. Gather User Inputs read -p "Enter your Let's Encrypt Email: " USER_EMAIL if [ -z "$USER_EMAIL" ]; then echo "Error: Email cannot be empty."; exit 1; fi read -p "Enter base storage path (e.g., /opt/stack): " BASE_PATH if [ -z "$BASE_PATH" ]; then echo "Error: Storage path cannot be empty."; exit 1; fi read -p "Enter your root domain (e.g., example.com): " ROOT_DOMAIN if [ -z "$ROOT_DOMAIN" ]; then echo "Error: Domain cannot be empty."; exit 1; fi echo "-------------------------------------" echo " Container Name Customization " echo "-------------------------------------" read -p "Traefik container name [Proxy]: " C_TRAEFIK C_TRAEFIK=${C_TRAEFIK:-Proxy} read -p "Redis container name [Redis]: " C_REDIS C_REDIS=${C_REDIS:-Redis} read -p "MySQL container name [mysql]: " C_MYSQL C_MYSQL=${C_MYSQL:-mysql} read -p "Nextcloud container name [NameOfYourContainer]: " C_NEXTCLOUD C_NEXTCLOUD=${C_NEXTCLOUD:-NameOfYourContainer} read -p "Portainer container name [Container-WebUI]: " C_PORTAINER C_PORTAINER=${C_PORTAINER:-Container-WebUI} read -p "phpMyAdmin container name [Database-WebUI]: " C_PHPMYADMIN C_PHPMYADMIN=${C_PHPMYADMIN:-Database-WebUI} echo "-------------------------------------" echo " Database & Admin Configuration " echo "-------------------------------------" read -p "Enter MySQL Root Password: " MYSQL_ROOT_PASS if [ -z "$MYSQL_ROOT_PASS" ]; then echo "Error: Password cannot be empty."; exit 1; fi read -p "Enter Target Database Name [nextclouddb]: " MYSQL_DB MYSQL_DB=${MYSQL_DB:-nextclouddb} read -p "Enter MySQL Nextcloud User Name [nc_user]: " MYSQL_USER MYSQL_USER=${MYSQL_USER:-nc_user} read -p "Enter MySQL Nextcloud User Password: " MYSQL_PASS if [ -z "$MYSQL_PASS" ]; then echo "Error: Password cannot be empty."; exit 1; fi read -p "Enter Nextcloud Admin Username [admin]: " NC_ADMIN NC_ADMIN=${NC_ADMIN:-admin} read -p "Enter Nextcloud Admin Password: " NC_ADMIN_PASS if [ -z "$NC_ADMIN_PASS" ]; then echo "Error: Password cannot be empty."; exit 1; fi # Clean trailing slashes if any from path BASE_PATH=$(echo "$BASE_PATH" | sed 's:/*$::') echo "" echo "Configuring environment..." echo "-> Email: $USER_EMAIL" echo "-> Base Location: $BASE_PATH" echo "-> Database Name: $MYSQL_DB" echo "-> Portainer Domain: portainer.$ROOT_DOMAIN" echo "-> Nextcloud Domain: nextcloud.$ROOT_DOMAIN" echo "=====================================" # 2. Setup local directory structures & required Traefik files echo "Creating local directories..." mkdir -p "$BASE_PATH/Traefik/files" mkdir -p "$BASE_PATH/Portainer" mkdir -p "$BASE_PATH/phpmyadmin/theme" mkdir -p "$BASE_PATH/Nextcloud/data" mkdir -p "$BASE_PATH/mysql/data" # Traefik requires acme.json to exist and have strict 600 permissions if [ ! -f "$BASE_PATH/Traefik/acme.json" ]; then echo "Creating empty acme.json with correct permissions..." touch "$BASE_PATH/Traefik/acme.json" chmod 600 "$BASE_PATH/Traefik/acme.json" fi # Create a blank traefik.yml config file if it doesn't exist if [ ! -f "$BASE_PATH/Traefik/files/traefik.yml" ]; then touch "$BASE_PATH/Traefik/files/traefik.yml" fi # Create a blank phpmyadmin config if it doesn't exist if [ ! -f "$BASE_PATH/phpmyadmin/config.user.inc.php" ]; then touch "$BASE_PATH/phpmyadmin/config.user.inc.php" fi # 3. Ensure the external Docker network exists echo "Checking Docker network 'web'..." sudo docker network inspect web >/dev/null 2>&1 || sudo docker network create web # 4. Write the final docker-compose.yml file echo "Generating docker-compose.yml..." cat << EOF > docker-compose.yml volumes: redis_data: driver: local redis: driver: local letsencrypt: driver: local phpmyadmin: driver: local mysql_data: driver: local services: traefik: image: traefik:latest container_name: ${C_TRAEFIK} restart: unless-stopped command: - "--log.level=DEBUG" - "--accesslog=true" - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=true" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - "--entrypoints.web.http.redirections.entryPoint.scheme=https" -