#!/bin/bash # Exit on any error set -e 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 " 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 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" 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'..." docker network inspect web >/dev/null 2>&1 || 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: Proxy 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" - "--certificatesresolvers.myresolver.acme.httpchallenge=true" - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" - "--certificatesresolvers.myresolver.acme.email=${USER_EMAIL}" - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" ports: - 80:80 - 443:443 - 8081:8080 networks: - web volumes: - /var/run/docker.sock:/var/run/docker.sock - letsencrypt:/letsencrypt - ${BASE_PATH}/Traefik/acme.json:/acme.json - ${BASE_PATH}/Traefik/files/:/etc/traefik/traefik.yml redis: image: redis:latest container_name: Redis restart: unless-stopped networks: - web volumes: - redis:/var/lib/redis - redis_data:/data labels: - traefik.enable=true - traefik.backend=redis mysql: image: mysql:8.0 container_name: mysql restart: unless-stopped networks: - web environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASS} - MYSQL_DATABASE=mysql - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASS} volumes: - ${BASE_PATH}/mysql/data:/var/lib/mysql NextCloud: image: nextcloud:latest container_name: NameOfYourContainer restart: unless-stopped depends_on: - mysql - redis networks: - web labels: - traefik.protocol=http - traefik.port=80 - traefik.http.routers.nextcloud.middlewares=nextcloud,nextcloud_redirect - traefik.http.routers.nextcloud.tls=true - traefik.http.routers.nextcloud.entrypoints=websecure - traefik.http.routers.nextcloud.tls.certresolver=myresolver - traefik.http.routers.nextcloud.rule=Host(\`nextcloud.${ROOT_DOMAIN}\`) - traefik.http.middlewares.nextcloud.headers.customFrameOptionsValue=ALLOW-FR> - traefik.http.middlewares.nextcloud.headers.stsSeconds=155520011 - traefik.http.middlewares.nextcloud.headers.stsIncludeSubdomains=true - traefik.http.middlewares.nextcloud.headers.stsPreload=true - traefik.http.middlewares.nextcloud.redirectregex.regex=/.well-know> - traefik.http.middlewares.nextcloud_redirect.redirectregex.replacement=/remo> environment: - MYSQL_HOST=mysql - MYSQL_DATABASE=mysql - MYSQL_USER=${MYSQL_USER} - MYSQL_PASSWORD=${MYSQL_PASS} - NEXTCLOUD_ADMIN_USER=${NC_ADMIN} - NEXTCLOUD_ADMIN_PASSWORD=${NC_ADMIN_PASS} - REDIS_HOST=redis - NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.${ROOT_DOMAIN} - TRUSTED_PROXIES=172.18.0.0/16 volumes: - ${BASE_PATH}/Nextcloud/data/:/var/www/html portainer: image: portainer/portainer-ce:latest container_name: Container-WebUI command: -H unix:///var/run/docker.sock restart: always labels: - "traefik.enable=true" - "traefik.http.routers.portainer.entrypoints=web" - "traefik.http.routers.portainer.rule=Host(\`portainer.${ROOT_DOMAIN}\`)" - "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https" - "traefik.http.routers.portainer.middlewares=portainer-https-redirect" - "traefik.http.routers.portainer-secure.entrypoints=websecure" - "traefik.http.routers.portainer-secure.rule=Host(\`portainer.${ROOT_DOMAIN}\`)" - "traefik.http.routers.portainer-secure.tls=true" - "traefik.http.routers.portainer-secure.tls.certresolver=myresolver" - "traefik.http.routers.portainer-secure.service=portainer" - "traefik.http.services.portainer.loadbalancer.server.port=9000" - "traefik.docker.network=traefik-proxy" volumes: - /var/run/docker.sock:/var/run/docker.sock - ${BASE_PATH}/Portainer:/data ports: - 9001:9001 - 8081:9000 networks: - web phpmyadmin: image: phpmyadmin/phpmyadmin container_name: Database-WebUI restart: unless-stopped networks: - web environment: - PMA_ARBITRARY=1 labels: - traefik.backend=phpmyadmin - traefik.http.routers.phpmyadmin.tls.certresolver=myresolver - traefik.http.routers.phpmyadmin.rule=Host(\`phpmyadmin.${ROOT_DOMAIN}\`) - traefik.docker.network=web - traefik.port=80 volumes: - phpmyadmin:/sessions - ${BASE_PATH}/phpmyadmin/config.user.inc.php:/etc/phpmyadmin/config.user.inc.php - ${BASE_PATH}/phpmyadmin/theme/:/www/themes/theme/ networks: web: external: true EOF echo "Done! 'docker-compose.yml' has been created successfully." echo "You can now run: docker compose up -d"