first commit

This commit is contained in:
Menno van Leeuwen 2024-11-16 04:43:13 +01:00
commit a92e665f35
Signed by: vleeuwenmenno
SSH Key Fingerprint: SHA256:OJFmjANpakwD3F2Rsws4GLtbdz1TJ5tkQF0RZmF0TRE
23 changed files with 1053 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
**/data/
.env

View File

@ -0,0 +1,149 @@
name: arr-stack
services:
radarr:
container_name: radarr
image: lscr.io/linuxserver/radarr:latest
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
ports:
- 7878:7878
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- ./data/radarr-config:/config
- /mnt:/storage
restart: "unless-stopped"
networks:
- shared_network
sonarr:
image: linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
volumes:
- ./data/sonarr-config:/config
- /mnt:/storage
ports:
- 8989:8989
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
networks:
- shared_network
lidarr:
image: linuxserver/lidarr:latest
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
ports:
- 8686:8686
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- ./data/lidarr-config:/config
- /mnt:/storage
restart: unless-stopped
networks:
- shared_network
whisparr:
image: ghcr.io/hotio/whisparr:latest
environment:
- TZ=Europe/Amsterdam
ports:
- 8386:6969
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- ./data/whisparr-config:/config
- /mnt:/storage
restart: unless-stopped
networks:
- shared_network
prowlarr:
container_name: prowlarr
image: linuxserver/prowlarr:latest
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
volumes:
- ./data/prowlarr-config:/config
extra_hosts:
- host.docker.internal:host-gateway
ports:
- 9696:9696
restart: unless-stopped
networks:
- shared_network
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- LOG_HTML=${LOG_HTML:-false}
- CAPTCHA_SOLVER=${CAPTCHA_SOLVER:-none}
- TZ=Europe/Amsterdam
ports:
- "8191:8191"
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
networks:
- shared_network
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- JELLYFIN_PublishedServerUrl=https://jellyfin.vleeuwen.me
volumes:
- ./data/jellyfin-config:/config
- /mnt:/mnt
ports:
- 8096:8096
- 8920:8920
- 7359:7359/udp
- 1900:1900/udp
restart: unless-stopped
group_add:
- "992"
- "44"
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
networks:
- shared_network
overseerr:
image: lscr.io/linuxserver/overseerr:latest
container_name: overseerr
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
volumes:
- ./data/overseerr-config:/config
ports:
- 5555:5055
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
networks:
- shared_network
networks:
shared_network:
external: true
name: shared_network

View File

@ -0,0 +1,18 @@
name: codeserver
services:
server:
image: lscr.io/linuxserver/code-server:latest
container_name: code-server
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
- HASHED_PASSWORD=
- SUDO_PASSWORD_HASH=
- DEFAULT_WORKSPACE=/config/workspace
volumes:
- ./data:/config
- /mnt:/config/workspace
ports:
- 8443:8443
restart: unless-stopped

View File

@ -0,0 +1,25 @@
name: duplicati
services:
app:
image: lscr.io/linuxserver/duplicati:latest
environment:
- PUID=1
- PGID=1
- TZ=Europe/Amsterdam
- CLI_ARGS=
volumes:
- /mnt/services/duplicati/data:/config
- /mnt:/mnt
ports:
- 8200:8200
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
notifications:
image: ghcr.io/vleeuwenmenno/duplicati-discord-notification:main
ports:
- 5334:5000
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped

View File

@ -0,0 +1,20 @@
name: factorio
services:
server-manager:
image: "ofsm/ofsm:latest"
container_name: "factorio-server-manager"
restart: "unless-stopped"
environment:
- PUID=1000
- PGID=1000
- "FACTORIO_VERSION=stable"
- "RCON_PASS=458fc84534"
ports:
- "5080:80"
- "34197:34197/udp"
volumes:
- "/mnt/services/factorio/data/fsm-data:/opt/fsm-data"
- "/mnt/services/factorio/data/factorio-data/saves:/opt/factorio/saves"
- "/mnt/services/factorio/data/factorio-data/mods:/opt/factorio/mods"
- "/mnt/services/factorio/data/factorio-data/config:/opt/factorio/config"
- "/mnt/services/factorio/data/factorio-data/mod_packs:/opt/fsm/mod_packs"

View File

@ -0,0 +1,98 @@
# Example configuration file, it's safe to copy this as the default config file without any modification.
# You don't have to copy this file to your instance,
# just run `./act_runner generate-config > config.yaml` to generate a config file.
log:
# The level of logging, can be trace, debug, info, warn, error, fatal
level: info
runner:
# Where to store the registration result.
file: .runner
# Execute how many tasks concurrently at the same time.
capacity: 1
# Extra environment variables to run jobs.
envs:
A_TEST_ENV_NAME_1: a_test_env_value_1
A_TEST_ENV_NAME_2: a_test_env_value_2
# Extra environment variables to run jobs from a file.
# It will be ignored if it's empty or the file doesn't exist.
env_file: .env
# The timeout for a job to be finished.
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
# So the job could be stopped by the Gitea instance if it's timeout is shorter than this.
timeout: 3h
# Whether skip verifying the TLS certificate of the Gitea instance.
insecure: false
# The timeout for fetching the job from the Gitea instance.
fetch_timeout: 5s
# The interval for fetching the job from the Gitea instance.
fetch_interval: 2s
# The labels of a runner are used to determine which jobs the runner can run, and how to run them.
# Like: "macos-arm64:host" or "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"
# Find more images provided by Gitea at https://gitea.com/gitea/runner-images .
# If it's empty when registering, it will ask for inputting labels.
# If it's empty when execute `daemon`, will use labels in `.runner` file.
labels:
- "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"
- "ubuntu-22.04:docker://gitea/runner-images:ubuntu-22.04"
- "ubuntu-20.04:docker://gitea/runner-images:ubuntu-20.04"
cache:
# Enable cache server to use actions/cache.
enabled: true
# The directory to store the cache data.
# If it's empty, the cache data will be stored in $HOME/.cache/actcache.
dir: ""
# The host of the cache server.
# It's not for the address to listen, but the address to connect from job containers.
# So 0.0.0.0 is a bad choice, leave it empty to detect automatically.
host: ""
# The port of the cache server.
# 0 means to use a random available port.
port: 0
# The external cache server URL. Valid only when enable is true.
# If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself.
# The URL should generally end with "/".
external_server: ""
container:
# Specifies the network to which the container will connect.
# Could be host, bridge or the name of a custom network.
# If it's empty, act_runner will create a network automatically.
network: ""
# Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
privileged: false
# And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
options:
# The parent directory of a job's working directory.
# NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically.
# If the path starts with '/', the '/' will be trimmed.
# For example, if the parent directory is /path/to/my/dir, workdir_parent should be path/to/my/dir
# If it's empty, /workspace will be used.
workdir_parent:
# Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob
# You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.
# For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:
# valid_volumes:
# - data
# - /src/*.json
# If you want to allow any volume, please use the following configuration:
# valid_volumes:
# - '**'
valid_volumes: []
# overrides the docker client host with the specified one.
# If it's empty, act_runner will find an available docker host automatically.
# If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
# If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work.
docker_host: ""
# Pull docker image(s) even if already present
force_pull: true
# Rebuild docker image(s) even if already present
force_rebuild: false
host:
# The parent directory of a job's working directory.
# If it's empty, $HOME/.cache/act/ will be used.
workdir_parent:

50
gitea/docker-compose.yml Normal file
View File

@ -0,0 +1,50 @@
name: gittea
services:
server:
image: gitea/gitea:latest
restart: always
environment:
- PUID=1000
- PGID=1000
volumes:
- /mnt/services/gitea/data/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3030:3000"
- "22:22"
networks:
- net
postgres:
image: postgres:15-alpine
restart: always
environment:
- PUID=1000
- PGID=1000
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
volumes:
- /mnt/services/gitea/data/postgres:/var/lib/postgresql/data
networks:
- net
act_runner:
image: gitea/act_runner:latest
volumes:
- ./act-runner-config.yaml:/config.yaml
- /var/run/docker.sock:/var/run/docker.sock
environment:
- PUID=1000
- PGID=1000
- GITEA_INSTANCE_URL=https://git.mvl.sh
- GITEA_RUNNER_REGISTRATION_TOKEN=lIlte9POlu7aBanhCh3Xm1SPfohrexyfxqs9Yiqz
- GITEA_RUNNER_NAME=act-worker
- CONFIG_FILE=/config.yaml
restart: always
networks:
- net
networks:
net:

11
golink/docker-compose.yml Normal file
View File

@ -0,0 +1,11 @@
name: golink
services:
server:
image: ghcr.io/tailscale/golink:main
environment:
- PUID=1000
- PGID=1000
- TS_AUTHKEY=${TS_AUTHKEY}
volumes:
- /mnt/services/golink/data:/home/nonroot
restart: "unless-stopped"

84
immich/docker-compose.yml Normal file
View File

@ -0,0 +1,84 @@
name: immich
services:
server:
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
#extends:
# file: hwaccel.transcoding.yml
# service: nvenc # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
environment:
- PUID=1000
- PGID=1000
restart: always
healthcheck:
disable: false
machine-learning:
# For hardware acceleration, add one of -[armnn, cuda, openvino] to the image tag.
# Example tag: ${IMMICH_VERSION:-release}-cuda
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda
#extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
# file: hwaccel.ml.yml
# service: cuda # set to one of [armnn, cuda, openvino, openvino-wsl] for accelerated inference - use the `-wsl` version for WSL2 where applicable
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
healthcheck:
disable: false
redis:
container_name: immich_redis
image: docker.io/redis:6.2-alpine@sha256:2ba50e1ac3a0ea17b736ce9db2b0a9f6f8b85d4c27d5f5accc6a416d8f42c6d5
healthcheck:
test: redis-cli ping || exit 1
restart: always
database:
container_name: immich_postgres
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
environment:
PUID: 1000
PGID: 1000
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
healthcheck:
test: pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
interval: 5m
start_interval: 30s
start_period: 5m
command:
[
'postgres',
'-c',
'shared_preload_libraries=vectors.so',
'-c',
'search_path="$$user", public, vectors',
'-c',
'logging_collector=on',
'-c',
'max_wal_size=2GB',
'-c',
'shared_buffers=512MB',
'-c',
'wal_compression=on',
]
restart: always
volumes:
model-cache:

21
immich/hwaccel.ml.yml Normal file
View File

@ -0,0 +1,21 @@
# Configurations for hardware-accelerated machine learning
# If using Unraid or another platform that doesn't allow multiple Compose files,
# you can inline the config for a backend by copying its contents
# into the immich-machine-learning service in the docker-compose.yml file.
# See https://immich.app/docs/features/ml-hardware-acceleration for info on usage.
services:
cpu: {}
cuda:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities:
- gpu

View File

@ -0,0 +1,26 @@
# Configurations for hardware-accelerated transcoding
# If using Unraid or another platform that doesn't allow multiple Compose files,
# you can inline the config for a backend by copying its contents
# into the immich-microservices service in the docker-compose.yml file.
# See https://immich.app/docs/features/hardware-transcoding for more info on using hardware transcoding.
services:
cpu: {}
nvenc:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities:
- gpu
- compute
- video
vaapi:
devices:
- /dev/dri:/dev/dri

View File

@ -0,0 +1,79 @@
name: minecraft
services:
upnp:
image: ghcr.io/vleeuwenmenno/auto-upnp:latest
restart: unless-stopped
network_mode: host
environment:
UPNP_DURATION: 86400 # 24 hours in seconds
PORTS: |
[
{"port": 25565, "protocol": "tcp"},
{"port": 25565, "protocol": "udp"},
{"port": 24454, "protocol": "udp"},
{"port": 3456, "protocol": "tcp"},
{"port": 19132, "protocol": "udp"}
]
paper:
image: itzg/minecraft-server
tty: true
stdin_open: true
ports:
- "25565:25565/tcp"
- "24454:24454/udp"
- "19132:19132/udp"
- "3456:8100/tcp"
environment:
PUID: 1000
PGID: 1000
EULA: "TRUE"
TYPE: "paper"
VERSION: 1.21.1
DIFFICULTY: "hard"
SERVER_NAME: "Paper Mostly Vanilla Server"
MOTD: "Paper Server (Supports 1.20.x and newer!)"
MEMORY: "32G"
MAX_PLAYERS: 32
VIEW_DISTANCE: 32
SPAWN_MONSTERS: true
SPAWN_ANIMALS: true
ENFORCE_SECURE_PROFILE: false
PLUGINS: |
https://cdn.modrinth.com/data/Jrmoreqs/versions/Ch2Vh0XL/AdvancedBackups-spigot-1.21-3.6.3.jar
https://cdn.modrinth.com/data/9eGKb6K1/versions/tA5pALYl/voicechat-bukkit-2.5.25.jar
https://cdn.modrinth.com/data/eBqOQXoA/versions/ndMZChDv/RecoveryTotem-1.0.1.jar
https://cdn.modrinth.com/data/fALzjamp/versions/ytBhnGfO/Chunky-Bukkit-1.4.28.jar
https://cdn.modrinth.com/data/P1OZGk5p/versions/ffAFJrjN/ViaVersion-5.1.1.jar
https://cdn.modrinth.com/data/NpvuJQoq/versions/kwAAl5BS/ViaBackwards-5.1.1.jar
https://cdn.modrinth.com/data/wKkoqHrH/versions/ohEXB7mE/Geyser-Spigot.jar
https://cdn.modrinth.com/data/Vebnzrzj/versions/cfNN7sys/LuckPerms-Bukkit-5.4.145.jar
https://cdn.modrinth.com/data/swbUV1cr/versions/DB0OeC5p/bluemap-5.4-spigot.jar
OPS: |
StarDebris
WHITELIST: |
StarDebris
Audi358
TechnikTake
MsPremium
Barny_8874
Ricky_2405
KinderKiller3000
ScherzkeksMiner
PauBau
QuickWitPhil
Draxonix
Zakomi
skintsoldier122
Krank4ever
Benjilami
Barny_8847
Destination456
xKizu
restart: unless-stopped
volumes:
- /mnt/services/minecraft/data:/data

2
minecraft/shell.sh Executable file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env bash
docker compose exec paper rcon-cli $@

39
plex/docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
services:
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=1000
- PGID=1000
- VERSION=docker
- PLEX_CLAIM=${PLEX_CLAIM}
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=compute,video,utility
volumes:
- /mnt/services/plex/data/plex:/config
- /mnt/movies:/movies
- /mnt/tvshows:/tvshows
- /mnt/music:/music
- /dev/dri/renderD128:/dev/dri/renderD128
restart: unless-stopped
tautulli:
image: lscr.io/linuxserver/tautulli:latest
container_name: tautulli
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
volumes:
- /mnt/services/plex/data/tautulli:/config
ports:
- 8181:8181
restart: unless-stopped
networks:
- shared_network
networks:
shared_network:
external: true
name: shared_network

11
proxy/docker-compose.yml Normal file
View File

@ -0,0 +1,11 @@
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
cap_add:
- NET_BIND_SERVICE
privileged: true
network_mode: host
volumes:
- /mnt/services/proxy/data:/data
- /mnt/services/proxy/data/letsencrypt:/etc/letsencrypt

View File

@ -0,0 +1,21 @@
services:
sabnzbd:
image: lscr.io/linuxserver/sabnzbd:latest
container_name: sabnzbd
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Amsterdam
volumes:
- /mnt/services/sabnzbd/data:/config
- /mnt:/storage
ports:
- 7788:8080
restart: unless-stopped
networks:
- shared_network
networks:
shared_network:
external: true
name: shared_network

View File

@ -0,0 +1,44 @@
name: satisfactory
services:
server:
hostname: 'satisfactory-server'
image: 'wolveix/satisfactory-server:latest'
restart: unless-stopped
volumes:
- './data/config:/config'
- './data/certs/live/satisfactory.mvl.sh/fullchain.pem:/config/gamefiles/FactoryGame/Certificates/cert_chain.pem'
- './data/certs/live/satisfactory.mvl.sh/privkey.pem:/config/gamefiles/FactoryGame/Certificates/private_key.pem'
environment:
- MAXPLAYERS=4
- PGID=1000
- PUID=1000
- ROOTLESS=false
- STEAMBETA=false
healthcheck:
test: [ "CMD", "bash", "/healthcheck.sh" ]
interval: 30s
timeout: 10s
retries: 3
start_period: 120s
depends_on:
certbot:
condition: service_completed_successfully
deploy:
resources:
reservations:
memory: 4G
limits:
memory: 8G
network_mode: 'host'
certbot:
image: certbot/certbot
command: certonly --standalone --non-interactive --agree-tos -m menno@vleeuwen.me -d satisfactory.mvl.sh
# Uncomment this when requesting a new certificate, make sure to disable nginx-proxy-manager first since this conflicts with port 80
# ports:
# - '80:80/tcp'
volumes:
- ./data/certs:/etc/letsencrypt
environment:
- CERTBOT_MAIL=menno@vleeuwen.me
- DOMAIN=satisfactory.mvl.sh

38
stash/docker-compose.yml Normal file
View File

@ -0,0 +1,38 @@
services:
stash:
image: stashapp/stash:latest
container_name: stash
restart: unless-stopped
ports:
- "9999:9999"
environment:
- PUID=1000
- PGID=1000
- STASH_STASH=/data/
- STASH_GENERATED=/generated/
- STASH_METADATA=/metadata/
- STASH_CACHE=/cache/
- STASH_PORT=9999
volumes:
- /etc/localtime:/etc/localtime:ro
## Keep configs, scrapers, and plugins here.
- /mnt/services/stash/data/config:/root/.stash
## Point this at your collection.
- /mnt/stash:/data
## This is where your stash's metadata lives
- /mnt/services/stash/data/metadata:/metadata
## Any other cache content.
- /mnt/services/stash/data/cache:/cache
## Where to store binary blob data (scene covers, images)
- /mnt/services/stash/data/blobs:/blobs
## Where to store generated content (screenshots,previews,transcodes,sprites)
- /mnt/services/stash/data/generated:/generated
networks:
- shared_network
networks:
shared_network:
external: true
name: shared_network

View File

@ -0,0 +1,8 @@
services:
mennovanleeuwen.nl:
restart: unless-stopped
image: nginx
ports:
- 4445:80
volumes:
- ./mennovanleeuwen.nl/dist:/usr/share/nginx/html

@ -0,0 +1 @@
Subproject commit 6ede2819a679c2e41120de3848d11c6613ab23bd

245
tool.sh Executable file
View File

@ -0,0 +1,245 @@
#!/usr/bin/env bash
# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch error in pipe chains
set -o pipefail
# Script constants
readonly SCRIPT_NAME=$(basename "${0}")
readonly SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
readonly COMPOSE_FILE="docker-compose.yml"
# Color constants - using tput for better terminal compatibility
if [[ -t 1 ]]; then
readonly COLOR_RESET="$(tput sgr0)"
readonly COLOR_INFO="$(tput setaf 6)" # Cyan
readonly COLOR_SUCCESS="$(tput setaf 2)" # Green
readonly COLOR_WARNING="$(tput setaf 3)" # Yellow
readonly COLOR_ERROR="$(tput setaf 1)" # Red
readonly COLOR_DEBUG="$(tput setaf 5)" # Magenta
else
readonly COLOR_RESET=""
readonly COLOR_INFO=""
readonly COLOR_SUCCESS=""
readonly COLOR_WARNING=""
readonly COLOR_ERROR=""
readonly COLOR_DEBUG=""
fi
show_spinner() {
local -r pid="$1"
local -r delay=0.1
local spinstr='/-\|'
printf " "
tput sc
while kill -0 "${pid}" 2>/dev/null; do
local temp=${spinstr#?}
tput rc
printf "%s[%c]%s" "${COLOR_WARNING}" "${spinstr}" "${COLOR_RESET}"
local spinstr=${temp}${spinstr%"${temp}"}
sleep "${delay}"
done
tput rc
printf " \r"
}
log() {
local -r level="$1"
local -r message="$2"
local -r timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local -r use_printf="${3:-false}"
local color
case "${level}" in
INFO) color="${COLOR_INFO}" ;;
SUCCESS) color="${COLOR_SUCCESS}" ;;
WARNING) color="${COLOR_WARNING}" ;;
ERROR) color="${COLOR_ERROR}" ;;
DEBUG) color="${COLOR_DEBUG}" ;;
*) color="${COLOR_RESET}" ;;
esac
if [[ "${use_printf}" == true ]]; then
printf "%s[%s] [%s]:%s %s" "${color}" "${timestamp}" "${level}" "${COLOR_RESET}" "${message}"
else
printf "%s[%s] [%s]:%s %s\n" "${color}" "${timestamp}" "${level}" "${COLOR_RESET}" "${message}"
fi
}
log_info() { log "INFO" "$1" "${2:-false}"; }
log_success() { log "SUCCESS" "$1" "${2:-false}"; }
log_warning() { log "WARNING" "$1" "${2:-false}"; }
log_error() { log "ERROR" "$1" "${2:-false}"; }
log_debug() { log "DEBUG" "$1" "${2:-false}"; }
# Error handler
trap 'error_handler $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]:-})' ERR
error_handler() {
local -r exit_code="$1"
local -r line_number="$2"
local -r bash_lineno="$3"
local -r command="$4"
local -r function_trace="$5"
local -r error_message="Error in ${SCRIPT_NAME}: line ${line_number}, command '${command}' exited with status ${exit_code}"
log_error "${error_message}"
exit "${exit_code}"
}
# Docker compose wrapper
docker_compose() {
local -r compose_file="$1"
local -r command="$2"
if ! docker compose -f "${compose_file}" "${command}" &>/dev/null; then
log_error "Failed to execute: docker compose -f ${compose_file} ${command}"
return 1
fi
}
get_container_stats() {
local running_count=0
local not_running_count=0
local partially_running_count=0
while IFS= read -r -d '' dir; do
local compose_path="${dir}/${COMPOSE_FILE}"
if [[ -f "${compose_path}" ]]; then
local dir_name=$(basename "${dir}")
# Get expected container count from compose file
local expected_count
expected_count=$(grep -c "image:" "${compose_path}" 2>/dev/null || echo "0")
# Get actual running container count
local running_containers
running_containers=$(docker compose -f "${compose_path}" ps | grep -c "Up" 2>/dev/null || echo "0")
# Ensure counts are integers
expected_count=$(echo "$expected_count" | tr -cd '0-9')
running_containers=$(echo "$running_containers" | tr -cd '0-9')
# Default to 0 if empty
expected_count=${expected_count:-0}
running_containers=${running_containers:-0}
if [ "$expected_count" -eq "$running_containers" ]; then
log_success "${dir_name}: ${running_containers}/${expected_count}"
running_count=$((running_count + 1))
elif [ "$running_containers" -eq 0 ]; then
log_error "${dir_name}: ${running_containers}/${expected_count}"
not_running_count=$((not_running_count + 1))
else
log_warning "${dir_name}: ${running_containers}/${expected_count}"
partially_running_count=$((partially_running_count + 1))
fi
fi
done < <(find . -maxdepth 1 -type d -print0)
log_info "Summary:"
log_info " Fully running: ${running_count}"
log_info " Not running: ${not_running_count}"
log_info " Partially running: ${partially_running_count}"
}
list_containers() {
local -r show_status="${1:-false}"
log_info "Running containers: $(docker ps -q | wc -l)"
if [[ "${show_status}" == true ]]; then
get_container_stats
fi
}
stop_containers() {
log_warning "Stopping all containers"
while IFS= read -r -d '' dir; do
local compose_path="${dir}/${COMPOSE_FILE}"
if [[ -f "${compose_path}" ]]; then
local dir_name=$(basename "${dir}")
log_info "Stopping ${dir_name}" true
docker_compose "${compose_path}" down --remove-orphans & show_spinner $!
echo
fi
done < <(find . -maxdepth 1 -type d -print0)
list_containers
}
pull_containers() {
log_warning "Pulling images for all containers"
while IFS= read -r -d '' dir; do
local compose_path="${dir}/${COMPOSE_FILE}"
if [[ -f "${compose_path}" ]]; then
local dir_name=$(basename "${dir}")
log_info "Pulling ${dir_name}" true
docker_compose "${compose_path}" pull & show_spinner $!
echo
fi
done < <(find . -maxdepth 1 -type d -print0)
}
start_containers() {
log_warning "Starting up all containers"
while IFS= read -r -d '' dir; do
local compose_path="${dir}/${COMPOSE_FILE}"
if [[ -f "${compose_path}" ]]; then
local dir_name=$(basename "${dir}")
log_info "Starting ${dir_name}"
docker compose -f "${compose_path}" up -d
fi
done < <(find . -maxdepth 1 -type d -print0)
list_containers
}
show_usage() {
cat << EOF
Usage: ${SCRIPT_NAME} [options]
Docker container management script
Options:
-h, --help Show this help message
-p, --pull Pull the images for all containers
-s, --start Start all containers
-r, --restart Restart all containers
-u, --update Update all containers (stop, pull, start)
-d, --down Stop all containers
-l, --list List containers and their status
Examples:
${SCRIPT_NAME} --start # Start all containers
${SCRIPT_NAME} --update # Update all containers
${SCRIPT_NAME} --list # Show container status
EOF
}
main() {
local args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help) show_usage; exit 0 ;;
-p|--pull) pull_containers; exit 0 ;;
-s|--start) start_containers; exit 0 ;;
-r|--restart) stop_containers; start_containers; exit 0 ;;
-u|--update) stop_containers; pull_containers; start_containers; exit 0 ;;
-d|--down) stop_containers; exit 0 ;;
-l|--list) list_containers true; exit 0 ;;
*) log_error "Invalid option: $1"; show_usage; exit 1 ;;
esac
done
}
# Execute main function
main "$@"

View File

@ -0,0 +1,43 @@
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
networks:
- shared_network
ports:
- 6881:6881
- 6881:6881/udp
- 8085:8085
volumes:
- /mnt/services/torrent/data/gluetun:/gluetun
environment:
- PUID=1000
- PGID=1000
- VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
- OPENVPN_USER=${OPENVPN_USER}
- OPENVPN_PASSWORD=${OPENVPN_PASSWORD}
- SERVER_COUNTRIES=${SERVER_COUNTRIES}
restart: always
qbittorrent:
image: lscr.io/linuxserver/qbittorrent
container_name: qbittorrent
network_mode: "service:gluetun"
environment:
- PUID=1000
- PGID=1000
- WEBUI_PORT=8085
volumes:
- "/etc/localtime:/etc/localtime:ro"
- /mnt/services/torrent/data/qbit-config:/config
- /mnt:/storage
depends_on:
- gluetun
restart: always
networks:
shared_network:
external: true
name: shared_network

View File

@ -0,0 +1,18 @@
name: wireguard
services:
server:
image: lscr.io/linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- PEERS=s24,pc,laptop
volumes:
- /mnt/services/wireguard/data:/config
ports:
- 51820:51820/udp
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped