#!/bin/bash BLACK="\033[30m" RED="\033[31m" GREEN="\033[32m" YELLOW="\033[33m" BLUE="\033[34m" MAGENTA="\033[35m" CYAN="\033[36m" WHITE="\033[37m" RESET="\033[0m" colored() { echo -e "$1$2$RESET" } print_error() { echo "$(colored $RED "Error:") $1" >&2 } print_success() { colored $GREEN "$1" } print_usage() { echo -e "Usage: $0 [command]\n" echo "Commands:" echo " start: Start all services" echo " stop: Stop all services" echo " generate-env: Generate .env files for all services" echo " help: Show this help message" } check_docker() { if ! command -v docker &>/dev/null; then print_error "Docker is not installed." exit 1 fi } check_docker_compose() { check_docker local docker_compose_cmd="" if docker compose version &>/dev/null; then docker_compose_cmd="docker compose" elif command -v docker-compose &>/dev/null; then docker_compose_cmd="docker-compose" else print_error "Docker Compose is not installed." exit 1 fi DOCKER_COMPOSE_COMMAND=$docker_compose_cmd } generate_env_files() { cp --update=none ./gitea/.env.example ./gitea/.env cp --update=none ./sftpgo/.env.example ./sftpgo/.env cp --update=none ./vaultwarden/.env.example ./vaultwarden/.env cp --update=none ./caddy/.env.example ./caddy/.env cp --update=none ./glance/.env.example ./glance/.env cp --update=none ./ghost/.env.example ./ghost/.env cp --update=none ./immich/.env.example ./immich/.env cp --update=none ./uptime_kuma/.env.example ./uptime_kuma/.env cp --update=none ./croc/.env.example ./croc/.env cp --update=none ./stalwart/.env.example ./stalwart/.env cp --update=none ./crowdsec/.env.example ./crowdsec/.env cp --update=none ./prometheus/.env.example ./prometheus/.env cp --update=none ./caddy/Caddyfile.private.example ./caddy/Caddyfile.private print_success ".env files generated." } # Some containers run as a fixed non-root uid and bind-mount their data from # this repo. A fresh `git clone` (or a non-root restore) leaves that data owned # by the cloning user, so those services can't write to it. Docker does not # chown bind mounts, so reset ownership to each container's uid before starting. # Add a line here whenever a new service pins a non-root `user:`. fix_permissions() { echo "Fixing data ownership for fixed-uid services..." # Only services that run as a fixed non-root uid AND write to a bind-mounted # data dir need this. Root containers write fine; images like mysql/gitea # chown their own data via a root entrypoint; grafana/prometheus here only # read config or use no writable bind mount, so they are intentionally absent. sudo chown -R 1001:1001 ./stalwart/data # stalwart: compose user "1001:1001" sudo chown -R 1000:1000 ./sftpgo/data # sftpgo: image default user 1000 } start_services() { fix_permissions # Ensure ~/.docker/config.json is a FILE before any container mounts it. # watchtower bind-mounts it; if it is missing, the Docker daemon creates it # as a root-owned directory, which then breaks every docker CLI call. mkdir -p ~/.docker [ -d ~/.docker/config.json ] && sudo rmdir ~/.docker/config.json [ -f ~/.docker/config.json ] || echo '{}' > ~/.docker/config.json docker network create caddy 2>/dev/null docker network create grafana 2>/dev/null docker network create gitea 2>/dev/null echo "Starting prometheus..." $DOCKER_COMPOSE_COMMAND -f ./prometheus/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Prometheus started successfully." else print_error "failed to start Prometheus!" exit 1 fi echo "Starting Grafana..." $DOCKER_COMPOSE_COMMAND -f ./grafana/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Grafana started successfully." else print_error "failed to start Grafana!" exit 1 fi echo "Starting gitea..." $DOCKER_COMPOSE_COMMAND -f ./gitea/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Gitea started successfully." else print_error "failed to start Gitea!" exit 1 fi echo "Starting memos..." $DOCKER_COMPOSE_COMMAND -f ./memos/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Memos started successfully." else print_error "failed to start Memos!" exit 1 fi echo "Starting sftpgo..." $DOCKER_COMPOSE_COMMAND -f ./sftpgo/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Sftpgo started successfully." else print_error "failed to start Sftpgo!" exit 1 fi echo "Starting slash..." $DOCKER_COMPOSE_COMMAND -f ./slash/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Slash started successfully." else print_error "failed to start Slash!" exit 1 fi echo "Starting vaultwarden..." $DOCKER_COMPOSE_COMMAND -f ./vaultwarden/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Vaultwarden started successfully." else print_error "failed to start Vaultwarden!" exit 1 fi echo "Starting wg-easy..." $DOCKER_COMPOSE_COMMAND -f ./wg_easy/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Wg-easy started successfully." else print_error "failed to start Wg-easy!" exit 1 fi echo "Starting glance..." $DOCKER_COMPOSE_COMMAND -f ./glance/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Glance started successfully." else print_error "failed to start Glance!" exit 1 fi echo "Starting ghost..." $DOCKER_COMPOSE_COMMAND -f ./ghost/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Ghost started successfully." else print_error "failed to start Ghost!" exit 1 fi echo "Starting immich..." $DOCKER_COMPOSE_COMMAND -f ./immich/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Immich started successfully." else print_error "failed to start Immich!" exit 1 fi echo "Starting uptime kuma..." $DOCKER_COMPOSE_COMMAND -f ./uptime_kuma/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Uptime kuma started successfully." else print_error "failed to start Uptime kuma!" exit 1 fi echo "Starting croc..." $DOCKER_COMPOSE_COMMAND -f ./croc/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Croc started successfully." else print_error "failed to start Croc!" exit 1 fi echo "Starting crowdsec..." $DOCKER_COMPOSE_COMMAND -f ./crowdsec/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Crowdsec started successfully." else print_error "failed to start Crowdsec!" exit 1 fi echo "Starting caddy..." $DOCKER_COMPOSE_COMMAND -f ./caddy/docker-compose.yaml up --pull always --build -d if [ $? -eq 0 ]; then print_success "Caddy started successfully." else print_error "failed to start Caddy!" exit 1 fi echo "Starting watchtower..." $DOCKER_COMPOSE_COMMAND -f ./watchtower/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Watchtower started successfully." else print_error "failed to start Watchtower!" exit 1 fi echo "Starting stalwart..." $DOCKER_COMPOSE_COMMAND -f ./stalwart/docker-compose.yaml up --pull always -d if [ $? -ne 0 ]; then print_error "failed to start Stalwart!" exit 1 fi $DOCKER_COMPOSE_COMMAND -f ./stalwart/docker-compose.yaml run --rm --pull always stalwart-bootstrap if [ $? -eq 0 ]; then print_success "Stalwart started successfully." else print_error "Stalwart bootstrap failed!" exit 1 fi echo "Starting gopkg proxy..." $DOCKER_COMPOSE_COMMAND -f ./gopkg_proxy/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Gopkg proxy started successfully." else print_error "failed to start Gopkg proxy!" exit 1 fi echo "Starting ech0..." $DOCKER_COMPOSE_COMMAND -f ./ech0/docker-compose.yaml up --pull always -d if [ $? -eq 0 ]; then print_success "Ech0 started successfully." else print_error "failed to start Ech0!" exit 1 fi } stop_services() { echo "Stopping grafana..." $DOCKER_COMPOSE_COMMAND -f ./grafana/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Grafana stopped successfully." else print_error "failed to stop Grafana!" exit 1 fi echo "Stopping prometheus..." $DOCKER_COMPOSE_COMMAND -f ./prometheus/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Prometheus stopped successfully." else print_error "failed to stop Prometheus!" exit 1 fi echo "Stopping gitea..." $DOCKER_COMPOSE_COMMAND -f ./gitea/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Gitea stopped successfully." else print_error "failed to stop Gitea!" exit 1 fi echo "Stopping memos..." $DOCKER_COMPOSE_COMMAND -f ./memos/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Memos stopped successfully." else print_error "failed to stop Memos!" exit 1 fi echo "Stopping sftpgo..." $DOCKER_COMPOSE_COMMAND -f ./sftpgo/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Sftpgo stopped successfully." else print_error "failed to stop Sftpgo!" exit 1 fi echo "Stopping slash..." $DOCKER_COMPOSE_COMMAND -f ./slash/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Slash stopped successfully." else print_error "failed to stop Slash!" exit 1 fi echo "Stopping vaultwarden..." $DOCKER_COMPOSE_COMMAND -f ./vaultwarden/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Vaultwarden stopped successfully." else print_error "failed to stop Vaultwarden!" exit 1 fi echo "Stopping wg-easy..." $DOCKER_COMPOSE_COMMAND -f ./wg_easy/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Wg-easy stopped successfully." else print_error "failed to stop Wg-easy!" exit 1 fi echo "Stopping glance..." $DOCKER_COMPOSE_COMMAND -f ./glance/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Glance stopped successfully." else print_error "failed to stop Glance!" exit 1 fi echo "Stopping ghost..." $DOCKER_COMPOSE_COMMAND -f ./ghost/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Ghost stopped successfully." else print_error "failed to stop Ghost!" exit 1 fi echo "Stopping immich..." $DOCKER_COMPOSE_COMMAND -f ./immich/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Immich stopped successfully." else print_error "failed to stop Immich!" exit 1 fi echo "Stopping uptime kuma..." $DOCKER_COMPOSE_COMMAND -f ./uptime_kuma/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Uptime kuma stopped successfully." else print_error "failed to stop Uptime kuma!" exit 1 fi echo "Stopping croc..." $DOCKER_COMPOSE_COMMAND -f ./croc/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Croc stopped successfully." else print_error "failed to stop Croc!" exit 1 fi echo "Stopping caddy..." $DOCKER_COMPOSE_COMMAND -f ./caddy/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Caddy stopped successfully." else print_error "failed to stop Caddy!" exit 1 fi echo "Stopping crowdsec..." $DOCKER_COMPOSE_COMMAND -f ./crowdsec/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Crowdsec stopped successfully." else print_error "failed to stop Crowdsec!" exit 1 fi echo "Stopping watchtower..." $DOCKER_COMPOSE_COMMAND -f ./watchtower/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Watchtower stopped successfully." else print_error "failed to stop Watchtower!" exit 1 fi echo "Stopping stalwart..." $DOCKER_COMPOSE_COMMAND -f ./stalwart/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Stalwart stopped successfully." else print_error "failed to stop Stalwart!" exit 1 fi echo "Stopping gopkg proxy..." $DOCKER_COMPOSE_COMMAND -f ./gopkg_proxy/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Gopkg proxy stopped successfully." else print_error "failed to stop Gopkg proxy!" exit 1 fi echo "Stopping ech0..." $DOCKER_COMPOSE_COMMAND -f ./ech0/docker-compose.yaml down if [ $? -eq 0 ]; then print_success "Ech0 stopped successfully." else print_error "failed to stop ech0!" exit 1 fi } if [ $# -lt 1 ]; then print_usage exit 1 fi case $1 in start) check_docker_compose start_services ;; stop) check_docker_compose stop_services ;; generate-env) generate_env_files ;; help) print_usage ;; *) print_error "Invalid command: $1" print_usage exit 1 ;; esac