From 1ef24b3be8ed494d7a05d96ac3113e99a2eecb98 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Wed, 15 Apr 2026 10:02:41 +0800 Subject: [PATCH] improve: best-practice configs for all projects, CLI UX overhaul Compose improvements: - forgejo: add healthcheck (/api/healthz), ROOT_URL + SSH_PORT env, LFS - tailscale: drop redundant privileged (use cap_add only), use devices for /dev/net/tun, mount /lib/modules, reliable healthcheck (tailscale status), profiles for opt-in DERP, headscale comment in .env.example - uptime-kuma: add built-in healthcheck (extra/healthcheck) - filesuite: add healthchecks for both cloudreve and qbittorrent - minecraft: add mc-health check (built into itzg image), simplify volumes - teamspeak: add healthcheck via ServerQuery (nc localhost 10011) - nextcloud: add healthchecks for all 3 services, depends_on with service_healthy conditions so startup order is correct CLI improvements: - Fix docker compose detection (was broken with space in arg) - Use global array for project discovery (no word-splitting bugs) - Empty selection no longer defaults to "all" (safety) - Show .env.example comments as hints during interactive configure - Required fields (empty default) loop until user provides a value - Disable colors when stdout is not a terminal - compose() wrapper auto-adds --env-file - Deduplicate project_exists / project_dir helpers --- README.md | 12 +- automa | 250 +++++++++++++++++++++++---------------- filesuite/.env.example | 10 ++ filesuite/compose.yaml | 13 ++ forgejo/.env.example | 6 + forgejo/compose.yaml | 13 +- minecraft/.env.example | 3 + minecraft/compose.yaml | 8 +- nextcloud/.env.example | 7 ++ nextcloud/compose.yaml | 23 +++- tailscale/.env.example | 10 ++ tailscale/compose.yaml | 19 ++- teamspeak/.env.example | 2 + teamspeak/compose.yaml | 6 + uptime-kuma/.env.example | 3 + uptime-kuma/compose.yaml | 6 + 16 files changed, 267 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 19af9b0..43562d1 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ cd ~/automa ```bash ./automa deploy # interactive project selection -./automa deploy forgejo monitoring # deploy specific projects +./automa deploy forgejo filesuite # deploy specific projects ./automa status # check all project status ./automa logs forgejo # follow logs ./automa stop forgejo # stop a project -./automa update monitoring # pull latest images & recreate +./automa update nextcloud # pull latest images & recreate ./automa config tailscale # reconfigure .env ./automa list # list available projects ``` @@ -29,15 +29,11 @@ cd ~/automa |---------|-------------| | `forgejo` | Self-hosted Git (Gitea fork) | | `uptime-kuma` | Uptime monitoring dashboard | -| `tailscale` | Tailscale client + DERP relay server | -| `monitoring` | Prometheus + Grafana + Blackbox + Node Exporter | +| `tailscale` | Tailscale client + DERP relay server (profiles) | | `filesuite` | Cloudreve cloud storage + qBittorrent | | `minecraft` | Fabric Minecraft server | | `teamspeak` | TeamSpeak voice server | | `nextcloud` | Nextcloud with MariaDB + Redis | -| `huajibot` | HuaJi Bot | -| `dockge` | Docker Compose stack manager | -| `notification-center` | Webhook notification service | ## Structure @@ -46,7 +42,7 @@ Each project is a self-contained directory: ``` project-name/ ├── compose.yaml # Docker Compose definition -├── .env.example # Template with default values +├── .env.example # Template — comments shown during setup └── .env # Your config (gitignored, created by CLI) ``` diff --git a/automa b/automa index 2e3dfd3..a379ff5 100755 --- a/automa +++ b/automa @@ -3,11 +3,7 @@ # # Install & run: # curl -fsSL https://raw.githubusercontent.com/m1ngsama/automa/main/install.sh | bash -# automa deploy -# -# Or clone and run directly: -# git clone https://github.com/m1ngsama/automa.git && cd automa -# ./automa deploy +# cd ~/automa && ./automa deploy set -euo pipefail @@ -17,15 +13,13 @@ set -euo pipefail AUTOMA_VERSION="1.0.0" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -CYAN='\033[0;36m' -BOLD='\033[1m' -DIM='\033[2m' -NC='\033[0m' +# Colors (disabled when not a terminal) +if [[ -t 1 ]]; then + RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' + CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' +else + RED='' GREEN='' YELLOW='' CYAN='' BOLD='' DIM='' NC='' +fi # ============================================================================ # Helpers @@ -33,7 +27,6 @@ NC='\033[0m' info() { echo -e "${GREEN}[+]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } error() { echo -e "${RED}[-]${NC} $*" >&2; } -dim() { echo -e "${DIM}$*${NC}"; } banner() { echo "" @@ -42,90 +35,145 @@ banner() { echo "" } -require_cmd() { - if ! command -v "$1" &>/dev/null; then - error "$1 is required but not installed." - [[ -n "${2:-}" ]] && dim " Install: $2" +check_docker() { + if ! command -v docker &>/dev/null; then + error "docker is required but not installed." + echo -e " ${DIM}Install: https://docs.docker.com/engine/install/${NC}" + exit 1 + fi + if ! docker compose version &>/dev/null 2>&1; then + error "docker compose plugin is required." + echo -e " ${DIM}Install: https://docs.docker.com/compose/install/${NC}" exit 1 fi } # ============================================================================ # Project discovery -# Scans for top-level dirs containing compose.yaml # ============================================================================ +PROJECTS=() + discover_projects() { - local projects=() + PROJECTS=() for dir in "$SCRIPT_DIR"/*/; do - [[ -f "$dir/compose.yaml" ]] && projects+=("$(basename "$dir")") + [[ -f "$dir/compose.yaml" ]] && PROJECTS+=("$(basename "$dir")") done - echo "${projects[@]}" +} + +project_exists() { + local name="$1" + [[ -f "$SCRIPT_DIR/$name/compose.yaml" ]] +} + +project_dir() { + echo "$SCRIPT_DIR/$1" +} + +# compose wrapper that auto-adds --env-file if .env exists +compose() { + local name="$1"; shift + local dir="$SCRIPT_DIR/$name" + local args=(-f "$dir/compose.yaml") + + if [[ -f "$dir/.env" ]]; then + args+=(--env-file "$dir/.env") + fi + + docker compose "${args[@]}" "$@" } # ============================================================================ # Interactive .env configuration -# Reads .env.example, prompts user for each value, writes .env # ============================================================================ configure_env() { - local project_dir="$1" - local env_example="$project_dir/.env.example" - local env_file="$project_dir/.env" + local name="$1" + local dir="$SCRIPT_DIR/$name" + local env_example="$dir/.env.example" + local env_file="$dir/.env" if [[ ! -f "$env_example" ]]; then warn "No .env.example found, skipping configuration" return 0 fi + # If .env already exists, ask what to do if [[ -f "$env_file" ]]; then echo "" - echo -e " ${YELLOW}.env already exists.${NC}" - echo -e " ${DIM}[k]eep [r]econfigure [v]iew${NC}" + echo -e " ${YELLOW}.env already exists for ${BOLD}${name}${NC}" + echo -e " ${DIM}[k]eep current [r]econfigure [v]iew${NC}" while true; do read -rp " > " choice - case "$choice" in + case "${choice,,}" in k|keep|"") info "Keeping existing .env"; return 0 ;; r|reconfigure) break ;; v|view) echo "" while IFS= read -r line; do - echo -e " ${DIM}${line}${NC}" + echo -e " ${DIM}${line}${NC}" done < "$env_file" echo "" echo -e " ${DIM}[k]eep [r]econfigure${NC}" ;; - *) echo -e " ${DIM}k/r/v${NC}" ;; + *) echo -e " ${DIM}Enter k, r, or v${NC}" ;; esac done fi echo "" - info "Configure ${BOLD}$(basename "$project_dir")${NC}" - dim " Enter values (blank = use default shown in brackets)" + echo -e " ${BOLD}Configure: ${CYAN}${name}${NC}" + echo "" + + # Show header comments from .env.example + while IFS= read -r line; do + [[ "$line" =~ ^#.* ]] && echo -e " ${DIM}${line}${NC}" || break + done < "$env_example" + echo "" + echo -e " ${DIM}Enter values (blank = accept default in brackets)${NC}" echo "" local tmp_env tmp_env="$(mktemp)" + local pending_comment="" while IFS= read -r line; do - # Skip comments and empty lines - [[ "$line" =~ ^#.* ]] && continue - [[ -z "$line" ]] && continue + # Blank line + if [[ -z "$line" ]]; then + [[ -n "$pending_comment" ]] && { echo "" ; pending_comment=""; } + continue + fi + + # Comment line — show as hint for next variable + if [[ "$line" =~ ^#.* ]]; then + echo -e " ${DIM}${line}${NC}" + pending_comment="$line" + continue + fi local key="${line%%=*}" local default="${line#*=}" + pending_comment="" + local val if [[ -n "$default" ]]; then read -rp " ${key} [${default}]: " val echo "${key}=${val:-$default}" >> "$tmp_env" else - read -rp " ${key}: " val + # Required field — no default + while true; do + read -rp " ${key} (required): " val + if [[ -n "$val" ]]; then + break + fi + echo -e " ${RED}This field cannot be empty${NC}" + done echo "${key}=${val}" >> "$tmp_env" fi done < "$env_example" mv "$tmp_env" "$env_file" chmod 600 "$env_file" - info ".env written" + echo "" + info ".env saved (chmod 600)" } # ============================================================================ @@ -134,25 +182,24 @@ configure_env() { cmd_list() { banner - local projects - read -ra projects <<< "$(discover_projects)" + discover_projects - if [[ ${#projects[@]} -eq 0 ]]; then + if [[ ${#PROJECTS[@]} -eq 0 ]]; then warn "No projects found" return 1 fi echo -e " ${BOLD}Available projects:${NC}" echo "" + local i=1 - for p in "${projects[@]}"; do - local status="${DIM}not deployed${NC}" + for p in "${PROJECTS[@]}"; do + local status="${DIM}not configured${NC}" if [[ -f "$SCRIPT_DIR/$p/.env" ]]; then - # Check if compose is running - if docker compose -f "$SCRIPT_DIR/$p/compose.yaml" ps --status running 2>/dev/null | grep -q .; then + if compose "$p" ps --status running 2>/dev/null | grep -q .; then status="${GREEN}running${NC}" else - status="${YELLOW}configured${NC}" + status="${YELLOW}stopped${NC}" fi fi printf " ${BOLD}%2d${NC} %-24s %b\n" "$i" "$p" "$status" @@ -163,13 +210,10 @@ cmd_list() { cmd_deploy() { banner - require_cmd docker "https://docs.docker.com/engine/install/" - require_cmd "docker compose" || require_cmd docker-compose + check_docker + discover_projects - local projects - read -ra projects <<< "$(discover_projects)" - - if [[ ${#projects[@]} -eq 0 ]]; then + if [[ ${#PROJECTS[@]} -eq 0 ]]; then error "No projects found" return 1 fi @@ -186,36 +230,35 @@ cmd_deploy() { echo -e " ${BOLD}Select projects to deploy:${NC}" echo "" local i=1 - for p in "${projects[@]}"; do + for p in "${PROJECTS[@]}"; do printf " ${BOLD}%2d${NC} %s\n" "$i" "$p" ((i++)) done echo "" - echo -e " ${DIM}Enter numbers (space-separated), 'all', or 'q' to quit${NC}" + echo -e " ${DIM}Enter numbers (space-separated), or 'q' to quit${NC}" read -rp " > " selection - [[ "$selection" == "q" ]] && return 0 + [[ "$selection" == "q" || -z "$selection" ]] && return 0 local selected=() - if [[ "$selection" == "all" || -z "$selection" ]]; then - selected=("${projects[@]}") + if [[ "$selection" == "all" ]]; then + selected=("${PROJECTS[@]}") else for num in $selection; do - if [[ "$num" =~ ^[0-9]+$ ]] && ((num > 0 && num <= ${#projects[@]})); then - selected+=("${projects[$((num-1))]}") + if [[ "$num" =~ ^[0-9]+$ ]] && ((num > 0 && num <= ${#PROJECTS[@]})); then + selected+=("${PROJECTS[$((num-1))]}") else - warn "Invalid selection: $num" + warn "Invalid: $num (skipped)" fi done fi if [[ ${#selected[@]} -eq 0 ]]; then - error "No projects selected" - return 1 + return 0 fi echo "" - info "Deploying: ${selected[*]}" + info "Will deploy: ${selected[*]}" echo "" local ok=0 fail=0 @@ -225,76 +268,78 @@ cmd_deploy() { else ((fail++)) fi + echo "" done - echo "" - echo -e " ${BOLD}Summary${NC}" - [[ $ok -gt 0 ]] && info "Deployed: $ok" - [[ $fail -gt 0 ]] && error "Failed: $fail" + echo -e " ${BOLD}Done.${NC} ${GREEN}${ok} deployed${NC}" \ + "$( ((fail > 0)) && echo -e ", ${RED}${fail} failed${NC}" )" } deploy_project() { local name="$1" - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi echo -e " ${CYAN}── ${BOLD}${name}${NC} ${CYAN}──${NC}" - configure_env "$project_dir" + configure_env "$name" + + if [[ ! -f "$SCRIPT_DIR/$name/.env" ]]; then + error "No .env file — run: automa config $name" + return 1 + fi info "Starting containers..." - if docker compose -f "$project_dir/compose.yaml" --env-file "$project_dir/.env" up -d 2>&1; then - info "${name} deployed" + if compose "$name" up -d 2>&1; then + info "${name} is up" return 0 else - error "${name} deployment failed" + error "${name} failed to start" return 1 fi } cmd_stop() { local name="${1:?Usage: automa stop }" - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi info "Stopping ${name}..." - docker compose -f "$project_dir/compose.yaml" down + compose "$name" down info "${name} stopped" } cmd_logs() { local name="${1:?Usage: automa logs }" shift - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi - docker compose -f "$project_dir/compose.yaml" logs -f "$@" + compose "$name" logs -f "$@" } cmd_status() { banner - local projects - read -ra projects <<< "$(discover_projects)" + check_docker + discover_projects - for p in "${projects[@]}"; do - local dir="$SCRIPT_DIR/$p" + for p in "${PROJECTS[@]}"; do echo -e " ${BOLD}${p}${NC}" - if docker compose -f "$dir/compose.yaml" ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | tail -n +2 | grep -q .; then - docker compose -f "$dir/compose.yaml" ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | tail -n +2 | while IFS= read -r line; do + local output + output=$(compose "$p" ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null | tail -n +2) || true + if [[ -n "$output" ]]; then + while IFS= read -r line; do echo -e " ${line}" - done + done <<< "$output" else echo -e " ${DIM}not running${NC}" fi @@ -304,43 +349,40 @@ cmd_status() { cmd_restart() { local name="${1:?Usage: automa restart }" - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi info "Restarting ${name}..." - docker compose -f "$project_dir/compose.yaml" restart + compose "$name" restart info "${name} restarted" } cmd_config() { local name="${1:?Usage: automa config }" - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi - configure_env "$project_dir" + configure_env "$name" } cmd_update() { local name="${1:?Usage: automa update }" - local project_dir="$SCRIPT_DIR/$name" - if [[ ! -f "$project_dir/compose.yaml" ]]; then + if ! project_exists "$name"; then error "Project not found: $name" return 1 fi info "Pulling latest images for ${name}..." - docker compose -f "$project_dir/compose.yaml" pull + compose "$name" pull info "Recreating containers..." - docker compose -f "$project_dir/compose.yaml" --env-file "$project_dir/.env" up -d + compose "$name" up -d info "${name} updated" } @@ -351,25 +393,25 @@ cmd_help() { ${BOLD}Commands:${NC} deploy [project...] Interactive deploy (or specify project names) - list List all available projects - status Show running status of all projects - stop Stop a project + list List all available projects and status + status Show running containers per project + stop Stop a project (docker compose down) restart Restart a project logs Follow logs of a project - config Configure .env for a project + config (Re)configure .env for a project update Pull latest images and recreate help Show this help ${BOLD}Examples:${NC} automa deploy # interactive project selection - automa deploy forgejo monitoring # deploy specific projects + automa deploy forgejo filesuite # deploy specific projects automa status # check all project status automa logs forgejo # follow forgejo logs - automa update monitoring # pull & restart monitoring + automa update nextcloud # pull & restart nextcloud ${BOLD}Quick start:${NC} curl -fsSL https://raw.githubusercontent.com/m1ngsama/automa/main/install.sh | bash - cd automa && ./automa deploy + cd ~/automa && ./automa deploy EOF } diff --git a/filesuite/.env.example b/filesuite/.env.example index 111a329..2b26d3f 100644 --- a/filesuite/.env.example +++ b/filesuite/.env.example @@ -1,7 +1,17 @@ +# Filesuite - Cloudreve cloud storage + qBittorrent +# Both services share the same downloads directory + TZ=Asia/Shanghai PUID=1000 PGID=1000 + +# Shared downloads path (absolute path recommended) DOWNLOADS_DIR=./downloads + +# Cloudreve CLOUDREVE_PORT=5212 +CR_ENABLE_ARIA2=0 + +# qBittorrent QB_WEBUI_PORT=8090 QB_BT_PORT=44773 diff --git a/filesuite/compose.yaml b/filesuite/compose.yaml index 25ddeb6..f610a89 100644 --- a/filesuite/compose.yaml +++ b/filesuite/compose.yaml @@ -4,11 +4,18 @@ services: container_name: cloudreve environment: TZ: "${TZ:-Asia/Shanghai}" + CR_ENABLE_ARIA2: "${CR_ENABLE_ARIA2:-0}" volumes: - ./cloudreve-data:/cloudreve/data - ${DOWNLOADS_DIR:-./downloads}:/data/downloads ports: - "${CLOUDREVE_PORT:-5212}:5212" + healthcheck: + test: ["CMD-SHELL", "curl -fSs http://localhost:5212/ || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s restart: unless-stopped qbittorrent: @@ -26,4 +33,10 @@ services: - "${QB_WEBUI_PORT:-8090}:${QB_WEBUI_PORT:-8090}" - "${QB_BT_PORT:-44773}:${QB_BT_PORT:-44773}" - "${QB_BT_PORT:-44773}:${QB_BT_PORT:-44773}/udp" + healthcheck: + test: ["CMD-SHELL", "curl -fSs http://localhost:${QB_WEBUI_PORT:-8090}/ || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s restart: unless-stopped diff --git a/forgejo/.env.example b/forgejo/.env.example index 7d08e27..4447e3e 100644 --- a/forgejo/.env.example +++ b/forgejo/.env.example @@ -1,2 +1,8 @@ +# Forgejo - self-hosted Git service +# Docs: https://forgejo.org/docs/latest/admin/config-cheat-sheet/ + FORGEJO_HTTP_PORT=3000 FORGEJO_SSH_PORT=2223 + +# Set this to your public URL when behind a reverse proxy +FORGEJO_ROOT_URL=http://localhost:3000 diff --git a/forgejo/compose.yaml b/forgejo/compose.yaml index 1bd241f..4b47f87 100644 --- a/forgejo/compose.yaml +++ b/forgejo/compose.yaml @@ -3,8 +3,11 @@ services: image: codeberg.org/forgejo/forgejo:9 container_name: forgejo environment: - USER_UID: 1000 - USER_GID: 1000 + USER_UID: "${FORGEJO_UID:-1000}" + USER_GID: "${FORGEJO_GID:-1000}" + FORGEJO__server__ROOT_URL: "${FORGEJO_ROOT_URL:-http://localhost:3000}" + FORGEJO__server__SSH_PORT: "${FORGEJO_SSH_PORT:-2223}" + FORGEJO__server__LFS_START_SERVER: "true" ports: - "${FORGEJO_HTTP_PORT:-3000}:3000" - "${FORGEJO_SSH_PORT:-2223}:22" @@ -12,4 +15,10 @@ services: - ./data:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro + healthcheck: + test: ["CMD", "curl", "-fSs", "http://localhost:3000/api/healthz"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 30s restart: unless-stopped diff --git a/minecraft/.env.example b/minecraft/.env.example index d28f530..e755a1e 100644 --- a/minecraft/.env.example +++ b/minecraft/.env.example @@ -1,3 +1,6 @@ +# Minecraft server (itzg/minecraft-server) +# Docs: https://docker-minecraft-server.readthedocs.io/ + TZ=Asia/Shanghai MC_TYPE=FABRIC MC_VERSION=1.21.1 diff --git a/minecraft/compose.yaml b/minecraft/compose.yaml index bbb7c30..06c4419 100644 --- a/minecraft/compose.yaml +++ b/minecraft/compose.yaml @@ -17,8 +17,12 @@ services: TZ: "${TZ:-Asia/Shanghai}" volumes: - ./data:/data - - ./mods:/data/mods - - ./configs:/configs:ro + healthcheck: + test: mc-health + interval: 30s + timeout: 10s + retries: 5 + start_period: 120s restart: unless-stopped tty: true stdin_open: true diff --git a/nextcloud/.env.example b/nextcloud/.env.example index d7023c1..920658d 100644 --- a/nextcloud/.env.example +++ b/nextcloud/.env.example @@ -1,10 +1,17 @@ +# Nextcloud with MariaDB + Redis +# Docs: https://hub.docker.com/_/nextcloud + TZ=Asia/Shanghai NC_PORT=8080 NC_ADMIN_USER=admin NC_ADMIN_PASSWORD= NC_TRUSTED_DOMAINS=localhost + +# MariaDB MYSQL_DATABASE=nextcloud MYSQL_USER=nextcloud MYSQL_PASSWORD= MYSQL_ROOT_PASSWORD= + +# Redis REDIS_PASSWORD= diff --git a/nextcloud/compose.yaml b/nextcloud/compose.yaml index 9c75206..87ea942 100644 --- a/nextcloud/compose.yaml +++ b/nextcloud/compose.yaml @@ -3,8 +3,10 @@ services: image: nextcloud:stable-apache container_name: nextcloud depends_on: - - db - - redis + db: + condition: service_healthy + redis: + condition: service_healthy ports: - "${NC_PORT:-8080}:80" environment: @@ -23,6 +25,12 @@ services: - nc-data:/var/www/html/data - nc-config:/var/www/html/config - nc-apps:/var/www/html/custom_apps + healthcheck: + test: ["CMD", "curl", "-fSs", "http://localhost/status.php"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 60s restart: unless-stopped db: @@ -37,6 +45,12 @@ services: MYSQL_PASSWORD: "${MYSQL_PASSWORD}" volumes: - nc-db:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s restart: unless-stopped redis: @@ -45,6 +59,11 @@ services: command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"] volumes: - nc-redis:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 10s + timeout: 5s + retries: 3 restart: unless-stopped volumes: diff --git a/tailscale/.env.example b/tailscale/.env.example index f817da7..954a75b 100644 --- a/tailscale/.env.example +++ b/tailscale/.env.example @@ -1,9 +1,19 @@ +# Tailscale + DERP relay server +# +# Deploy tailscale only: docker compose --profile tailscale up -d +# Deploy with DERP: docker compose --profile derp up -d + TZ=Asia/Shanghai TS_HOSTNAME= TS_AUTHKEY= + +# For headscale: --advertise-tags=tag:container --login-server=https://your.headscale.host TS_EXTRA_ARGS=--advertise-tags=tag:container + TS_USERSPACE=false TS_FIREWALL_MODE=nftables + +# DERP relay (only needed with --profile derp) DERP_HOST= DERP_PORT=443 STUN_PORT=3478 diff --git a/tailscale/compose.yaml b/tailscale/compose.yaml index 912e136..12e1018 100644 --- a/tailscale/compose.yaml +++ b/tailscale/compose.yaml @@ -3,15 +3,13 @@ services: image: tailscale/tailscale:latest container_name: tailscale hostname: "${TS_HOSTNAME}" - volumes: - - ./tailscale-data:/var/lib/tailscale - - /dev/net/tun:/dev/net/tun - - /var/run/tailscale:/var/run/tailscale - privileged: true + profiles: ["tailscale", "derp"] cap_add: - NET_ADMIN - SYS_MODULE - NET_RAW + devices: + - /dev/net/tun:/dev/net/tun network_mode: host environment: TS_AUTHKEY: "${TS_AUTHKEY}" @@ -22,13 +20,22 @@ services: TS_DEBUG_FIREWALL_MODE: "${TS_FIREWALL_MODE:-nftables}" TS_HOSTNAME: "${TS_HOSTNAME}" TZ: "${TZ:-Asia/Shanghai}" + volumes: + - ./tailscale-data:/var/lib/tailscale + - /var/run/tailscale:/var/run/tailscale + - /lib/modules:/lib/modules:ro healthcheck: - test: ["CMD-SHELL", "tailscale status --json | grep -q '\"BackendState\": \"Running\"'"] + test: ["CMD-SHELL", "tailscale status"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s restart: unless-stopped derp-server: image: ghcr.io/nbtca/tailscale-derp:edge container_name: tailscale-derp + profiles: ["derp"] network_mode: host depends_on: tailscale: diff --git a/teamspeak/.env.example b/teamspeak/.env.example index ed0d791..293d461 100644 --- a/teamspeak/.env.example +++ b/teamspeak/.env.example @@ -1 +1,3 @@ +# TeamSpeak voice server + TS3_ADMIN_PASSWORD= diff --git a/teamspeak/compose.yaml b/teamspeak/compose.yaml index 3e1a3ca..b6e4e20 100644 --- a/teamspeak/compose.yaml +++ b/teamspeak/compose.yaml @@ -11,6 +11,12 @@ services: TS3SERVER_SERVERADMIN_PASSWORD: "${TS3_ADMIN_PASSWORD}" volumes: - ts-data:/var/ts3server/ + healthcheck: + test: ["CMD-SHELL", "echo quit | nc localhost 10011 | grep -q TS3"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s restart: unless-stopped volumes: diff --git a/uptime-kuma/.env.example b/uptime-kuma/.env.example index 19a30b0..ac0cc39 100644 --- a/uptime-kuma/.env.example +++ b/uptime-kuma/.env.example @@ -1 +1,4 @@ +# Uptime Kuma - uptime monitoring +# Default binds to localhost only; change to 0.0.0.0:3001 for external access + KUMA_PORT=127.0.0.1:3001 diff --git a/uptime-kuma/compose.yaml b/uptime-kuma/compose.yaml index 36ec80a..c383016 100644 --- a/uptime-kuma/compose.yaml +++ b/uptime-kuma/compose.yaml @@ -6,4 +6,10 @@ services: - ./data:/app/data ports: - "${KUMA_PORT:-127.0.0.1:3001}:3001" + healthcheck: + test: ["CMD-SHELL", "extra/healthcheck"] + interval: 60s + timeout: 30s + retries: 5 + start_period: 180s restart: unless-stopped