This commit is contained in:
Mike McFetridge
2026-07-20 09:23:17 -04:00
parent c1315882da
commit 72272e4006
3179 changed files with 562960 additions and 14 deletions
@@ -0,0 +1,54 @@
version: '3.6'
services:
ollama:
# Uncomment below for GPU support
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# count: 1
# capabilities:
# - gpu
volumes:
- ollama:/root/.ollama
ports:
- 11434:11434
container_name: ollama
pull_policy: always
tty: true
restart: unless-stopped
image: ollama/ollama:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
ollama-webui:
build:
context: .
args:
OLLAMA_API_BASE_URL: '/ollama/api'
image: ollama-webui:latest
container_name: ollama-webui
depends_on:
- ollama
ports:
- 3000:8080
environment:
- "OLLAMA_API_BASE_URL=http://ollama:11434/api"
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
volumes:
ollama: {}
@@ -0,0 +1,23 @@
services:
# Ollama - Local Open-source AI Models
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
profiles: ["apps", "all"]
networks:
- default
ports:
- "$OLLAMA_PORT:11434"
volumes:
- $DOCKERDIR/appdata/ollama:/root/.ollama
# deploy:
# resources:
# reservations:
# devices:
# - driver: nvidia
# device_ids: ['all']
# capabilities: [gpu]
environment:
- OLLAMA_KEEP_ALIVE=24h
# DOCKER-LABELS-PLACEHOLDER
@@ -0,0 +1,5 @@
1. Clone the repo from: https://github.com/ollama-webui/ollama-webui
2. Tweak the docker-compose to your liking
3. Run the container: sudo docker compose up -d
Let it build :)
@@ -0,0 +1,27 @@
services:
# Open-WebUI - User-friendly AI Interface
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
profiles: [ "apps", "all" ]
networks:
- default
depends_on:
- ollama
ports:
- "$OPENWEBUI_PORT:8080"
environment:
- OLLAMA_BASE_URL=http://$SERVER_LAN_IP:$OLLAMA_PORT
- WEBUI_SECRET_KEY=$OPENWEBUI_SECRET_KEY
# - CORS_ALLOW_ORIGIN=
- USER_AGENT=Open-WebUI
volumes:
- $DOCKERDIR/appdata/open-webui:/app/backend/data
# DOCKER-LABELS-PLACEHOLDER
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:3000" ]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
@@ -0,0 +1,20 @@
services:
searxng:
container_name: searxng
image: docker.io/searxng/searxng:latest
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- /opt/Docker-Deployments/searxng:/etc/searxng:rw
- /opt/Docker-Deployments/searxng/data:/var/cache/searxng:rw
environment:
- SEARXNG_BASE_URL=https://search.mikemcfetridge.com
- UWSGI_WORKERS=${SEARXNG_UWSGI_WORKERS:-4}
- UWSGI_THREADS=${SEARXNG_UWSGI_THREADS:-4}
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8080" ]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
@@ -0,0 +1,54 @@
##### My SearXNG personal settings. #
use_default_settings: true
general:
debug: false
instance_name: "McFetridge Private Search" # Changes the name on the homepage and browser tabs
brand:
docs_url: "" # Use empty strings instead of false
issue_url: "" # Hides the links without crashing the parser
custom:
links:
"Dockhand": "http://192.168.2.3:3200"
"My Dashboard": "http://192.168.2.15:3100"
server:
port: 8080
bind_address: "0.0.0.0"
secret_key: "ee072bb45348da92be54b5224bee58b982a4279d3531f056b727c4dd3397bc3e"
image_proxy: true # Proxies images through your server to protect your IP
search:
default_lang: "en-US" # Force results to a specific region
safe_search: 1 # 0 = None, 1 = Moderate, 2 = Strict
autocomplete: "duckduckgo" # Upstream source for search suggestions
formats:
- html
- json # Adds API capabilities for external script ingestion
ui:
theme: simple
results_on_new_tab: true # Keeps your engine open when browsing links
default_locale: "en"
default_interface: "general" # The default tab ("general", "images", "videos", "news", "it")
# This is the standard way to cleanly enable/disable specific engines in SearXNG
engines:
- name: google
disabled: true
- name: duckduckgo
disabled: false
- name: wikipedia
disabled: false
- name: wikidata
disabled: false
- name: bing
disabled: false
- name: brave
disabled: false
@@ -0,0 +1,13 @@
services:
it-tools:
image: 'corentinth/it-tools:latest'
ports:
- '8080:80'
restart: unless-stopped
container_name: it-tools
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
@@ -0,0 +1,9 @@
services:
myspeed:
container_name: MySpeed
image: germannewsmaker/myspeed
ports:
- '5216:5216'
volumes:
- ./myspeed:/myspeed/data
restart: unless-stopped
+70
View File
@@ -0,0 +1,70 @@
# Store environmental variables here. All variables are optional.
# Lines beginning in '#' are ignored.
# Can be either development, production or test
# NODE_ENV=production
# The port to expose the running application on
# PORT=4000
# If you've proved SSL certs, then can set HTTPS port
# SSL_PORT=4001
# The host that Dashy is running on, domain or IP
# HOST=localhost
# The default base path for serving up static assets
# BASE_URL=./
# Optionally, specify the path of SSL private + public keys
# SSL_PRIV_KEY_PATH=/etc/ssl/certs/dashy-priv.key
# SSL_PUB_KEY_PATH=/etc/ssl/certs/dashy-pub.pem
# If SSL enabled, choose whether or not to redirect http to https
# Defaults to true
# REDIRECT_HTTPS=true
# The path to the user data directory
# USER_DATA_DIR=user-data
# Enable HTTP basic auth to protect your *.yml config files
# ENABLE_HTTP_AUTH=true
# Enable basic HTTP auth to protect your *.yml config files
# BASIC_AUTH_USERNAME
# BASIC_AUTH_PASSWORD
# If you'd like frontend to automatically authenticate when basic auth enabled, set credentials here too
# VUE_APP_BASIC_AUTH_USERNAME
# VUE_APP_BASIC_AUTH_PASSWORD
# Override where the path to the configuration file is, can be a remote URL
# VUE_APP_CONFIG_PATH=/conf.yml
# Usually the same as BASE_URL, but accessible in frontend
# VUE_APP_DOMAIN=https://dashy.to
# Override the page title for the frontend app
# VUE_APP_TITLE=''
# Set the default view to load on startup (can be `minimal`, `workspace` or `home`)
# VUE_APP_STARTING_VIEW=home
# Set the Vue app routing mode (can be 'hash', 'history' or 'abstract')
# VUE_APP_ROUTING_MODE=history
# Should enable SRI for build script and link resources
# INTEGRITY=true
# Computed automatically on build. Indicates if running in container
# IS_DOCKER=true
# Again, set automatically using package.json during build time
# VUE_APP_VERSION=2.0.0
# Directory for conf.yml backups
# BACKUP_DIR=./user-data/config-backups
# Setup any other user defined vars by prepending VUE_APP_ to the var name
# VUE_APP_pihole_ip=http://your.pihole.ip
# VUE_APP_pihole_key=your_pihole_secret_key
+297
View File
@@ -0,0 +1,297 @@
# Need to be stored in the dashy/config directory
pageInfo:
title: Hello, Miker
description: My Homepage of Services
logo: https://i.ibb.co/71WyyzM/little-bot-3.png
appConfig:
statusCheck: false
theme: glass-2
fontAwesomeKey: c94dc2b452
customCss: '.clock p.time { font-size: 3rem !important; }'
layout: vertical
iconSize: small
language: en
sections:
- name: My Public IP Address
widgets:
- type: public-ip
id: 0_1764_publicip
- name: Holidays
widgets:
- type: public-holidays
options:
country: US
holidayType: all
monthsToShow: 4
lang: en
id: 0_829_publicholidays
- name: Date and Time
widgets:
- type: clock
options:
timeZone: America/New_york
format: en-GB
hideDate: false
customCityName: Bealeton, Va.
use12Hour: true
hideSeconds: true
id: 0_396_clock
- name: RackNerd Server
items:
- title: Proxy Service
description: Nginx Proxy Manager
icon: icons/nginx-proxy-manager.png
url: https://proxy.mikeandrebel.com
target: newtab
statusCheck: true
- title: Search Engine
description: MIker's Search Engine
icon: icons/searxng.png
url: https://search.mikeandrebel.com/
target: newtab
statusCheck: true
- title: Portainer
description: Portainer
icon: icons/portainer.png
url: https://port.mikeandrebel.com/#!/home
target: newtab
statusCheck: true
- title: Rack Nerd Control Panel
description: Rack Nerd Control Panel
icon: icons/cloudflare.png
url: https://nerdvm.racknerd.com/login.php
target: newtab
- title: Rack Nerd
description: Rack Nerd
icon: icons/cloudflare.png
url: https://my.racknerd.com/index.php?rp=/login
target: newtab
- title: Mealie
description: Mealie
icon: icons/mealie.png
url: https://meal.mikeandrebel.com/
target: newtab
statusCheck: true
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Home Server 1
items:
- title: JellyFin
description: Jellyfin Movies
icon: icons/jellyfin.png
url: http://192.168.1.151:8096
target: newtab
- title: Local Proxy Server
description: Nginx Proxy Manager
icon: icons/nginx-proxy-manager.png
url: http://192.168.1.151:81/
target: newtab
- title: Portainer
description: Portainer
icon: icons/portainer.png
url: http://192.168.1.151:9000/
target: newtab
- title: Router
description: Orbi System
icon: icons/router.png
url: http://192.168.1.1/
target: newtab
- title: Router
description: Arris System
icon: icons/arris.png
url: http://192.168.0.1/
target: newtab
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Home Server 2 internal
items:
- title: Proxmox
icon: icons/proxmox.png
url: https://192.168.0.171:8006
target: newtab
statusCheck: true
- title: Portatiner
icon: icons/portainer.png
url: https://192.168.0.181:7443
target: newtab
- title: NPM
icon: icons/nginx-proxy-manager.png
url: http://192.168.0.235:81
target: newtab
- title: PiHole
icon: icons/pi-hole.png
url: http://192.168.0.185/admin/
target: newtab
- title: VSCode
icon: icons/vscode.png
url: http://192.168.0.187:8680
target: newtab
- title: Authelia
icon: icons/authelia.png
url: http://192.168.0.181:9091
target: newtab
- title: Dashy
icon: icons/dashy.png
url: http://192.168.0.181:4000
target: newtab
statusCheck: true
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Home Server External
items:
- title: Proxmox
icon: icons/proxmox.png
url: https://pve1.mikemcfetridge.com
target: newtab
statusCheck: true
- title: Potainer
icon: icons/portainer.png
url: https://port.mikemcfetridge.com
target: newtab
statusCheck: true
- title: NPM
icon: icons/nginx-proxy-manager.png
url: https://proxy.mikemcfetridge.com
target: newtab
- title: PiHole
icon: icons/pi-hole.png
url: https://pihole.mikemcfetridge.com
target: newtab
- title: VSCode
icon: icons/vscode.png
url: https://vscode.mikemcfetridge.com
target: newtab
- title: Authelia
icon: icons/authelia.png
url: https://auth.mikemcfetridge.com
target: newtab
- title: Dashy
icon: icons/dashy.png
url: https://dashy.mikemcfetridge.com
target: newtab
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Oracle Server
items:
- title: Hestia Control Panel
description: Hestia Control Panel
icon: icons/hestia.png
url: https://hcp.mcfetridge.us:8083
target: newtab
statusCheck: true
- title: Next Cloud
description: Next Cloud
icon: icons/nextcloud.png
url: https://next.mcfetridge.us
target: newtab
statusCheck: true
- title: Web Mail
description: Web Email
icon: si-reddit
url: https://webmail.mcfetridge.us
target: newtab
statusCheck: true
- title: Family Website
description: Wordpress Website
icon: icons/wordpress.png
url: https://mcfetridge.us/
target: newtab
statusCheck: true
- title: WordPress Admin
description: WordPress Admin
icon: icons/wordpress.png
url: https://mcfetridge.us/wp-admin
target: newtab
statusCheck: true
- title: Webstats
description: Hestia Webstats
icon: icons/hestia.png
url: https://mcfetridge.us/vstats/
target: newtab
statusCheck: true
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Oracle Test Server
items:
- title: Poratiner
description: Poratiner
icon: icons/portainer.png
url: http://
target: newtab
displayData:
sortBy: default
rows: 1
cols: 1
collapsed: true
hideForGuests: false
- name: Bookmarks
items:
- title: Name Cheap
description: Name Cheap
icon: icons/namecheap.png
url: https://www.namecheap.com/
target: newtab
- title: Oracle Cloud
description: Oracle Cloud
icon: icons/oracle-cloud.png
url: https://cloud.oracle.com/compute/instances?region=us-ashburn-1
target: newtab
- title: Send Blue
description: Send Blue STMP
icon: icons/sendinblue.png
url: https://app.brevo.com/
target: newtab
- title: Github
description: Git Hub
icon: icons/github.png
url: https://github.com/mmcfetridge1969
target: newtab
- title: DuckDNS
description: Duck DNS
icon: icons/duckdns.png
url: https://www.duckdns.org
displayData:
collapsed: true
hideForGuests: false
@@ -0,0 +1,56 @@
---
# Welcome to Dashy! To get started, run `docker compose up -d`
# You can configure your container here, by modifying this file
# If you need to download the Icons from GIT. Go to the root directory of Dashy >
# sudo git clone https://github.com/walkxcode/dashboard-icons.git
# This will create a dashboard-icons directory with all the png and svg files.
services:
dashy:
container_name: dashy
# Pull latest image
image: 'lissy93/dashy'
# To build from source, replace 'image: lissy93/dashy' with 'build: .'
# build: .
# You can also use an image with a different tag, or pull from a different r>
# image: ghcr.io/lissy93/dashy or image: lissy93/dashy:3.0.0
# Mapping data to the volume created for this service
volumes:
- /home/miker/docker/dashy/data/config/conf.yml:/app/user-data/conf.yml
- /home/miker/docker/dashy/png:/app/user-data/icons
# Set port that web service will be served on. Keep container port as 8080
# ports:
# - 4000:8080
# Set any environmental variables
environment:
- NODE_ENV=production
# Specify your user ID and group ID. You can find this by running `id -u` an>
- UID=1000 # Update to match your UID
- GID=1000 # Update to match your GID.
# Sets the service to restart always unless it is stopped manually.
restart: unless-stopped
# Configure healthchecks
healthcheck:
test: ['CMD', 'node', '/app/services/healthcheck']
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- proxy
# Service will be place in the proxy network for NPM security
networks:
proxy:
external: true
+27
View File
@@ -0,0 +1,27 @@
services:
# Dashy - Application Dashboard
dashy:
container_name: dashy
image: lissy93/dashy
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "all"]
networks:
- default
ports:
- $DASHY_PORT:8080
healthcheck:
test: ['CMD', 'node', '/app/services/healthcheck']
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
- $DOCKERDIR/appdata/dashy/conf.yml:/app/public/conf.yml
- $DOCKERDIR/appdata/dashy/item-icons:/app/public/item-icons
environment:
- NODE_ENV=production
- UID=$PUID
- GID=$PGID
# DOCKER-LABELS-PLACEHOLDER
+1
View File
@@ -0,0 +1 @@
ADGUARD_PASSWORD=P@ssw0rd
+439
View File
@@ -0,0 +1,439 @@
# icon: si:immich # si for Simple icons https://simpleicons.org/
# icon: sh:immich # sh for selfh.st icons https://selfh.st/icons/
# icon: di:immich # di for Dashboard icons https://github.com/homarr-labs/dashboard-icons
# icon: mdi:camera # mdi for Material Design icons https://pictogrammers.com/library/mdi/
pages:
- name: Home
hide-desktop-navigation: false
center-vertically: true
columns:
- size: small
widgets:
- type: calendar
- type: rss
limit: 10
collapse-after: 3
cache: 3h
feeds:
- url: https://omgubuntu.co.uk/feed
title: OmgUbuntu
limit: 4
- url: https://9to5linux.com/feed/atom
title: 9to5Linux
limit: 4
- type: twitch-channels
channels:
- technotim
- piratesoftware
- size: full
widgets:
- type: group
widgets:
- type: hacker-news
- type: lobsters
- type: search
autofocus: true
search-engine: google
new-tab: true
bangs:
- title: YouTube
shortcut: "!yt"
url: https://www.youtube.com/results?search_query={QUERY}
- title: Github
shortcut: "!gh"
url: https://github.com/search?q={QUERY}&type=repositories
- type: group
widgets:
- type: reddit
subreddit: technology
show-thumbnails: true
- type: reddit
subreddit: selfhosted
show-thumbnails: true
- type: videos
channels:
- UCVls1GmFKf6WlTraIb_IaJg # Distro Tube
- UCjSEJkpGbcZhvo0lr-44X_w # Tech Hut
- UCZNhwA1B5YqiY1nLzmM0ZRg # Christian Lempa
- UCVy16RS5eEDh8anP8j94G2A # DB TEch
- UCwFpzG5MK5Shg_ncAhrgr9g # Awesome Open Source
- UCxQKHvKbmSzGMvUrVtJYnUA # Learn Linux TV
- size: small
widgets:
- type: weather
location: Bealeton, Virginia, United States
units: imperial
hour-format: 12h # alternatively "24h"
- type: custom-api
title: Random Fact
cache: 6h
url: https://uselessfacts.jsph.pl/api/v2/facts/random
template: |
<p class="size-h4 color-paragraph">{{ .JSON.String "text" }}</p>
- type: custom-api
title: Immich stats
cache: 1d
url: http://192.168.2.13:2283/api/server/statistics
headers:
x-api-key: fYDkorU0ik0xOOvfwGiuYtcHWZmggaNYfvHARDTdWo
Accept: application/json
template: |
<div class="flex justify-between text-center">
<div>
<div class="color-highlight size-h3">{{ .JSON.Int "photos" | formatNumber }}</div>
<div class="size-h6">PHOTOS</div>
</div>
<div>
<div class="color-highlight size-h3">{{ .JSON.Int "videos" | formatNumber }}</div>
<div class="size-h6">VIDEOS</div>
</div>
<div>
<div class="color-highlight size-h3">{{ div (.JSON.Int "usage" | toFloat) 1073741824 | toInt | formatNumber }}GB</div>
<div class="size-h6">USAGE</div>
</div>
</div>
- type: releases
show-source-icon: true
repositories:
- go-gitea/gitea
- jellyfin/jellyfin
- glanceapp/glance
- dockerhub:gotify/server
- type: repository
repository: mmcfetridge1969/ansible
pull-requests-limit: 5
issues-limit: 3
commits-limit: 3
- type: repository
repository: ChristianLempa/boilerplates
pull-requests-limit: 5
issues-limit: 3
commits-limit: 3
- name: Monitoring
# Optionally, if you only have a single page you can hide the desktop navigation for a cleaner look
# hide-desktop-navigation: true
columns:
- size: small
widgets:
- type: calendar
- size: full
widgets:
- type: monitor
cache: 1m
title: Services
sites:
- title: Proxmox1
url: https://192.168.2.1:8006
check-url: https://192.168.2.1:8006
allow-insecure: true
icon: di:proxmox
- title: Proxmox2
url: https://192.168.2.2:8006
check-url: https://192.168.2.2:8006
allow-insecure: true
icon: di:proxmox
- title: Jellyfin
url: https://jellyfin.mikemcfetridge.com
check-url: http://192.168.2.16:8096
icon: di:jellyfin
- title: Immich
url: https://immich.mikemcfetridge.com
check-url: http://192.168.2.13:2283/
icon: di:immich
- title: Vaultwarden
url: https://vault.mikemcfetridge.com/
check-url: https://192.168.2.19:8000/
allow-insecure: true
icon: di:vaultwarden-light
- title: NGINX Proxy Manager
url: https://proxy.mikemcfetridge.com/
check-url: http://192.168.2.22:81/
icon: di:nginx-proxy-manager
- title: Wireguard
url: https://wg2.mikemcfetridge.com/
check-url: http://192.168.2.10:10086
icon: di:wireguard
- title: Gitea
url: https://gitea.mikemcfetridge.com/
check-url: http://192.168.2.24:3000/
icon: di:gitea
- title: IT-Tools
url: https://ittools.mikemcfetridge.com/
check-url: http://192.168.2.7:8089/
icon: di:it-tools
- title: Mealie
url: https://mealie.mikemcfetridge.com/
check-url: http://192.168.2.7:9925/
icon: di:mealie
- title: Wallabag
url: https://wall.mikemcfetridge.com
check-url: http://192.168.2.13:8667/
icon: di:wallabag
- title: Pi-Hole
url: http://192.168.2.8/admin
check-url: http://192.168.2.8/admin
icon: di:pi-hole
- title: Immich
url: https://immich.mikemcfetridge.com
check-url: http://192.168.2.13:2283/
icon: di:immich
- title: Searxng
url: https://search.mikemcfetridge.com
check-url: http://192.168.2.23:8888/
icon: di:searxng
- title: Authentik
url: https://authentik.mikemcfetridge.com
check-url: https://192.168.2.7:9443
allow-insecure: true
icon: di:authentik
- title: Portainer-1
url: https://port.mikemcfetridge.com
check-url: https://192.168.2.7:9445
allow-insecure: true
icon: di:portainer
- title: Portainer-2
url: https://192.168.2.13:9445
check-url: https://192.168.2.13:9445
allow-insecure: true
icon: di:portainer
- title: Tianji
url: https://tianji.mikemcfetridge.com/login
check-url: http://192.168.2.7:12345
allow-insecure: true
icon: di:tianji
- title: Technitium DNS
url: http://192.168.2.9:5380/
check-url: http://192.168.2.9:5380/
allow-insecure: true
icon: sh:technitium
- type: monitor
cache: 1m
title: Servarr
sites:
- title: Jellyseerr
url: http://192.168.2.16:5055
check-url: http://192.168.2.16:5055
icon: di:jellyseerr
- title: Homarr
url: https://dashboard.mikemcfetridge.com/
check-url: http://192.168.2.7:7575/
icon: di:homarr
- title: Radarr
url: http://192.168.2.16:7878
check-url: http://192.168.2.16:7878
icon: di:radarr
- title: Sonarr
url: http://192.168.2.16:8989
check-url: http://192.168.2.16:8989
icon: di:sonarr
- title: Lidarr
url: http://192.168.2.16:8686
check-url: http://192.168.2.16:8686
icon: di:lidarr
- title: Bazarr
url: http://192.168.2.16:6767
check-url: http://192.168.2.16:6767
icon: di:bazarr
- title: Prowlarr
url: http://192.168.2.16:9696
check-url: http://192.168.2.16:9696
icon: di:prowlarr
- title: qBittorrent
url: http://192.168.2.16:8118
check-url: http://192.168.2.16:8118
icon: di:qbittorrent
- title: Audio Book Shelf
url: https://audio.mikemcfetridge.com
check-url: http://192.168.2.16:13378
icon: di:audiobookshelf
- size: small
widgets:
- type: rss
limit: 10
collapse-after: 3
cache: 3h
feeds:
- url: https://omgubuntu.co.uk/feed
title: OmgUbuntu
limit: 4
- url: https://9to5linux.com/feed/atom
title: 9to5Linux
limit: 4
- name: Widget testing
# Optionally, if you only have a single page you can hide the desktop navigation for a cleaner look
# hide-desktop-navigation: true
columns:
- size: small
widgets:
- type: calendar
- size: full
widgets:
- type: custom-api
title: Netbird Devices
title-url: https://app.netbird.io/peers
url: https://api.netbird.io/api/peers
headers:
Accept: application/json
Authorization: Token ${nbp_bz2jNZN9MnkMErrBi8jFjrNsweZuY108oYSo}
cache: 10m
template: |
{{ $enableOnlineIndicator := false }}
<style>
.device-info-container {
position: relative;
overflow: hidden;
height: 1.5em;
}
.device-info {
display: flex;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.device-ip {
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
opacity: 0;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.device-info-container:hover .device-info {
transform: translateY(100%);
opacity: 0;
}
.device-info-container:hover .device-ip {
transform: translateY(0);
opacity: 1;
}
.offline-indicator,
.online-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
margin-left: 4px;
vertical-align: middle;
}
.online-indicator {
background-color: var(--color-positive);
}
.offline-indicator {
background-color: var(--color-negative);
}
.device-name-container {
display: flex;
align-items: center;
gap: 8px;
}
.indicators-container {
display: flex;
align-items: center;
gap: 4px;
}
</style>
<ul class="list list-gap-10 collapsible-container" data-collapse-after="4">
{{ range .JSON.Array "" }}
<li>
<div class="flex items-center gap-10">
<div class="device-name-container grow">
<span class="size-h4 block text-truncate color-primary">
{{ .String "hostname" }}
</span>
<div class="indicators-container">
{{ if .Bool "connected" }}
{{ if $enableOnlineIndicator }}
<span class="online-indicator" data-popover-type="text" data-popover-text="Online"></span>
{{ end }}
{{ else }}
{{ $lastSeen := .String "last_seen" | parseTime "rfc3339" }}
<span class="offline-indicator" data-popover-type="text" data-popover-text="Offline - Last seen {{ $lastSeen.Format "Jan 2 3:04pm" }}"></span>
{{ end }}
</div>
</div>
</div>
<div class="device-info-container">
<ul class="list-horizontal-text device-info">
<li>{{ .String "os" }}</li>
<li>{{ .String "city_name" }}, {{ .String "country_code"}}</li>
</ul>
<div class="device-ip">
{{ .String "ip" }}
{{ .String "dns_label"}}
</div>
</div>
</li>
{{ end }}
</ul>
- size: small
widgets:
- type: rss
limit: 10
collapse-after: 3
cache: 3h
feeds:
- url: https://omgubuntu.co.uk/feed
title: OmgUbuntu
limit: 4
- url: https://9to5linux.com/feed/atom
title: 9to5Linux
limit: 4
@@ -0,0 +1,17 @@
services:
glance:
image: glanceapp/glance # Uses the official Glance Docker image.
container_name: glance # Assigns a custom container name for easy management.
volumes:
- /home/miker/dockerapps/glance/config:/app/config # Mounts the glance.yml configuration file.
- /etc/timezone:/etc/timezone:ro # Ensures container timezone sync.
- /etc/localtime:/etc/localtime:ro # Ensures container localtime sync.
ports:
- 8280:8080 # Exposes Glance on port 8280.
restart: unless-stopped # Restarts container unless manually stopped.
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8280"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
+4
View File
@@ -0,0 +1,4 @@
PUID=1000
PGID=1000
# TZ for container timezone
TZ=America/New_York
+22
View File
@@ -0,0 +1,22 @@
---
services:
heimdall:
image: lscr.io/linuxserver/heimdall:2.6.1
container_name: heimdall
environment:
PUID: ${PUID} # Reference PUID from .env
PGID: ${PGID} # Reference PGID from .env
env_file:
- .env
volumes:
- ./heimdall/config:/config
ports:
- 8080:80
- 8443:443
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
+20
View File
@@ -0,0 +1,20 @@
services:
# Heimdall - Application Dashboard
heimdall:
image: lscr.io/linuxserver/heimdall:latest
container_name: heimdall
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "all"]
networks:
- default
ports:
- "$HEIMDALL_PORT:80"
volumes:
- $DOCKERDIR/appdata/heimdall:/config
environment:
PUID: $PUID
PGID: $PGID
TZ: $TZ
# DOCKER-LABELS-PLACEHOLDER
+4
View File
@@ -0,0 +1,4 @@
PUID=1000
PGID=1000
# TZ for container timezone
TZ=America/New_York
@@ -0,0 +1,3 @@
---
@@ -0,0 +1,516 @@
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz@9..40&family=Fira+Code&family=Poppins&family=Source+Code+Pro&family=Work+Sans&display=swap');
.theme-gray {
font-family: 'DM Sans', sans-serif;
/* DRACULA COLORS */
--dracula-background: #282a36;
--dracula-background-dark: #15161d;
--dracula-foreground: #44475a;
--dracula-text: #f8f8f2;
--dracula-slate: #6272a4;
--dracula-cyan: #8be9fd;
--dracula-green: #50fa7ae8;
--dracula-orange: #ffb86c;
--dracula-pink: #ff79c6;
--dracula-purple: #bd93f9;
--dracula-red: #ff5555;
--dracula-yellow: #f1fa8c;
/* Theme preset colors */
--color-50: 249 250 251;
--color-100: 243 244 246;
--color-200: 248 248 242; /* Text Color - use Dracula text color */
--color-300: 209 213 219;
--color-400: 156 163 175;
--color-500: 107 114 128;
--color-600: 75 85 99;
--color-700: 55 65 81;
--color-800: 40 42 54; /* Background color - use Dracula theme color */
--color-900: 21 22 29; /* Dracula specific */
--color-logo-start: 156 163 175;
--color-logo-stop: 55 65 81;
--standard-bg: #44475a8e;
/* INFO WIDGET COLORS */
--info-widgets: var(--dracula-purple);
--resource-bar-bg: var(--standard-bg);
--resource-bar-fg: var(--dracula-green);
--widget-border: var(--dracula-foreground);
/* SERVICES COLORS */
--service-group: var(--dracula-purple);
--service-name: var(--dracula-text);
--service-description: var(--dracula-purple);
--service-block-bg: #232530;
--service-block-text: var(--dracula-pink);
/* BOOKMARKS COLORS */
--bookmark-group: var(--dracula-purple);
--bookmark-icon-bg: #44475a60;
--bookmark-icon: var(--dracula-purple);
--bookmark-name: var(--dracula-text);
/* ALL CARD COLORS */
--card-color: #44475a46;
--card-color-hover: #44475a91;
/* FOOTER COLORS */
--footer-items: var(--dracula-pink);
/* FOOTER COLORS */
--scrollbar-fg: var(--dracula-purple);
--scrollbar-bg: var(--standard-bg);
/*Class color values */
.service-tags .dark\:bg-theme-900\/50 {
background-color: rgb(var(--color-900) / 0.3) !important;
}
/*******************************
* INFORMATION WIDGETS STYLES *
*******************************/
#information-widgets {
border-color: var(--widget-border);
}
#information-widgets * {
color: var(--info-widgets);
}
.resource-usage {
background-color: var(--resource-bar-bg);
}
.resource-usage > div {
background-color: var(--resource-bar-fg);
}
/*******************************
* SERVICES STYLES *
*******************************/
.service-group-icon > div {
background: var(--service-group) !important; /* group label ICON color */
}
.service-group-name {
color: var(--service-group); /* group label NAME color */
}
.services-group > button > svg {
color: var(--service-group); /* group label EXPAND/COLLAPSE ICON color */
}
.service-card {
background-color: var(--card-color);
}
.service-card:hover {
background-color: var(--card-color-hover);
}
.service-name.text-sm {
font-size: 0.95rem;
color: var(--service-name);
}
.service-description.text-xs {
font-size: 0.75rem;
color: var(--service-description);
}
.service img {
border-radius: 25%;
}
.service-block {
background: var(--service-block-bg);
}
.service-block .uppercase {
color: var(--service-block-text);
}
.service-block .font-thin {
color: var(--dracula-text);
}
/*******************************
* BOOKMARK STYLES *
*******************************/
.bookmark-group-name {
color: var(--bookmark-group);
}
.bookmark-icon {
background-color: var(--bookmark-icon-bg) !important;
}
.bookmark-icon > div > div {
background: var(
--bookmark-icon
) !important; /* If using si or md icons, make default purple */
}
.bookmark-name.text-xs {
font-size: 0.85rem;
color: var(--bookmark-name);
}
li.bookmark > a {
background-color: var(--card-color);
}
li.bookmark > a:hover {
background-color: var(--card-color-hover);
}
/*******************************
* CALENDAR STYLES *
*******************************/
#dracula-calendar .flex.justify-between.flex-wrap span {
color: var(--dracula-purple);
}
/*******************************
* FOOTER STYLES *
*******************************/
#footer svg {
color: var(--footer-items);
}
/*******************************
* SCROLLBAR STYLES *
*******************************/
* {
--scrollbar-thumb: var(--scrollbar-fg);
--scrollbar-track: var(--scrollbar-bg);
}
/*******************************
* GLANCES STYLES *
*******************************/
li[id^='glances-'] .recharts-surface > g:nth-of-type(1) path:nth-child(1) {
fill: var(--dracula-green);
fill-opacity: 0.15;
}
li[id^='glances-'] .recharts-surface g:nth-of-type(1) path:nth-child(2) {
stroke: var(--dracula-green);
stroke-opacity: 0.5;
}
li[id^='glances-'] .recharts-surface g:nth-of-type(2) path:nth-child(1) {
fill: var(--dracula-purple);
fill-opacity: 0.15;
}
li[id^='glances-'] .recharts-surface g:nth-of-type(2) path:nth-child(2) {
stroke: var(--dracula-purple);
stroke-opacity: 0.5;
}
li[id^='glances-'] .bottom-3.left-3 {
color: var(--dracula-pink);
}
li[id^='glances-'] .bottom-3.right-3 .opacity-75 {
color: var(--dracula-cyan);
opacity: 1;
font-size: 0.8rem;
}
li[id^='glances-'] .top-3.right-3 .opacity-50 {
color: var(--dracula-cyan);
opacity: 1;
font-size: 0.8rem;
}
li[id^='glances-'] .opacity-50 {
opacity: 0.8;
}
li[id^='glances-'] .flex.items-center.text-xs .text-right {
color: var(--dracula-cyan);
}
li[id^='glances-'] .flex.items-center .opacity-25.w-14.text-right {
color: var(--dracula-purple);
opacity: 0.85;
}
li[id^='glances-']
.bottom-4.right-3.left-3.z-20
.w-3.h-3.mr-1\.5.opacity-50
> div {
background: var(--dracula-green) !important;
opacity: 1;
}
li[id^='glances-'] .bottom-4.right-3.left-3.z-20 .opacity-75.grow {
color: var(--dracula-pink) !important;
opacity: 0.75;
}
/*******************************
* HOMEPAGE PRESETS *
*******************************/
.bg-amber-500 {
background-color: var(--dracula-orange);
}
.bg-black {
background-color: rgb(0 0 0);
}
.bg-blue-500 {
background-color: var(--dracula-cyan);
}
.bg-cyan-500 {
background-color: var(--dracula-cyan);
}
.bg-emerald-500 {
background-color: var(--dracula-green);
}
.bg-fuchsia-500 {
background-color: var(--dracula-pink);
}
.bg-gray-500 {
background-color: var(--dracula-foreground);
}
.bg-green-500 {
background-color: var(--dracula-green);
}
.bg-indigo-500 {
background-color: var(--dracula-purple);
}
.bg-lime-500 {
background-color: var(--dracula-green);
}
.bg-neutral-500 {
background-color: rgb(115 115 115);
}
.bg-orange-400 {
background-color: var(--dracula-orange);
}
.bg-orange-500 {
background-color: var(--dracula-orange);
}
.bg-pink-500 {
background-color: var(--dracula-pink);
}
.bg-purple-500 {
background-color: var(--dracula-purple);
}
.bg-red-500 {
background-color: var(--dracula-red);
}
.bg-rose-100 {
background-color: rgb(255, 205, 205);
}
.bg-rose-500 {
background-color: var(--dracula-red);
}
.bg-rose-900\/80 {
background-color: var(--dracula-red);
}
.bg-sky-500 {
background-color: var(--dracula-cyan);
}
.bg-slate-500 {
background-color: var(--dracula-slate);
}
.bg-stone-500 {
background-color: rgb(120 113 108);
}
.bg-teal-500 {
background-color: rgb(20 184 166);
}
.bg-violet-500 {
background-color: var(--dracula-purple);
}
.bg-white {
background-color: var(--dracula-text);
}
.bg-white\/50 {
background-color: hsla(0, 0%, 100%, 0.5);
}
.bg-yellow-500 {
background-color: var(--dracula-yellow);
}
.bg-zinc-500 {
background-color: rgb(113 113 122);
}
.text-amber-800 {
color: var(--dracula-orange);
}
.text-black {
color: rgb(0 0 0);
}
.text-black\/20 {
color: rgba(0, 0, 0, 0.2);
}
.text-blue-500\/80 {
color: rgba(139, 233, 253, 0.8);
}
.text-emerald-300 {
color: var(--dracula-green);
}
.text-emerald-500\/80 {
color: rgba(80, 250, 123, 0.8);
}
.text-gray-500 {
color: rgb(107 114 128);
}
.text-green-500 {
color: var(--dracula-green);
}
.text-orange-400\/50 {
color: rgba(255, 184, 108, 0.5);
}
.text-red-400 {
color: var(--dracula-red);
}
.text-red-500 {
color: var(--dracula-red);
}
.text-red-500\/40 {
color: rgba(255, 85, 85, 0.4);
}
.text-rose-300 {
color: var(--dracula-red);
}
.text-rose-500 {
color: var(--dracula-red);
}
.text-rose-500\/80 {
color: rgba(255, 85, 85, 0.8);
}
.text-rose-900 {
color: var(--dracula-red);
}
.text-white {
color: var(--dracula-text);
}
}
/* ********************************************************************************************* */
/* CARD COLOURING - DONE VIA "ID:" TAGS IN SERVICES.YAML */
.service-card::before {
content: "";
opacity: 0.40;
position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; z-index: 0;
}
/* DARK */
#red .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #812020; }
#yellow .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #818120; }
#green .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #208120; }
#cyan .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #208181; }
#blue .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #202081; }
#purple .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #812081; }
#black .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #000000; }
#white .dark\:hover\:bg-white\/10:is(.dark *).service-card::before { background-color: #ffffff; }
/* LIGHT */
#red .hover\:bg-theme-300\/20.service-card::before { background-color: #ff0000; }
#yellow .hover\:bg-theme-300\/20.service-card::before { background-color: #ffff00; }
#green .hover\:bg-theme-300\/20.service-card::before { background-color: #00ff00; }
#cyan .hover\:bg-theme-300\/20.service-card::before { background-color: #00ffff; }
#blue .hover\:bg-theme-300\/20.service-card::before { background-color: #0000ff; }
#purple .hover\:bg-theme-300\/20.service-card::before { background-color: #ff00ff; }
#black .hover\:bg-theme-300\/20.service-card::before { background-color: #ffffff; }
#white .hover\:bg-theme-300\/20.service-card::before { background-color: #000000; }
#orange .hover\:bg-theme-300\/20.service-card::before { background-color: #ffa500; }
#pink .hover\:bg-theme-300\/20.service-card::before { background-color: #ffc0cb; }
#teal .hover\:bg-theme-300\/20.service-card::before { background-color: #008080; }
#magenta .hover\:bg-theme-300\/20.service-card::before { background-color: #ff00ff; }
#lime .hover\:bg-theme-300\/20.service-card::before { background-color: #00ff00; }
#navy .hover\:bg-theme-300\/20.service-card::before { background-color: #000080; }
#maroon .hover\:bg-theme-300\/20.service-card::before { background-color: #800000; }
#olive .hover\:bg-theme-300\/20.service-card::before { background-color: #808000; }
#silver .hover\:bg-theme-300\/20.service-card::before { background-color: #c0c0c0; }
#gray .hover\:bg-theme-300\/20.service-card::before { background-color: #808080; }
#gold .hover\:bg-theme-300\/20.service-card::before { background-color: #ffd700; }
#coral .hover\:bg-theme-300\/20.service-card::before { background-color: #ff7f50; }
#salmon .hover\:bg-theme-300\/20.service-card::before { background-color: #fa8072; }
#indigo .hover\:bg-theme-300\/20.service-card::before { background-color: #4b0082; }
#violet .hover\:bg-theme-300\/20.service-card::before { background-color: #ee82ee; }
#turquoise .hover\:bg-theme-300\/20.service-card::before { background-color: #40e0d0; }
#plum .hover\:bg-theme-300\/20.service-card::before { background-color: #dda0dd; }
#orchid .hover\:bg-theme-300\/20.service-card::before { background-color: #da70d6; }
#tan .hover\:bg-theme-300\/20.service-card::before { background-color: #d2b48c; }
#beige .hover\:bg-theme-300\/20.service-card::before { background-color: #f5f5dc; }
#mint .hover\:bg-theme-300\/20.service-card::before { background-color: #98ff98; }
#lavender .hover\:bg-theme-300\/20.service-card::before { background-color: #e6e6fa; }
#chocolate .hover\:bg-theme-300\/20.service-card::before { background-color: #d2691e; }
#crimson .hover\:bg-theme-300\/20.service-card::before { background-color: #dc143c; }
#khaki .hover\:bg-theme-300\/20.service-card::before { background-color: #f0e68c; }
#aqua .hover\:bg-theme-300\/20.service-card::before { background-color: #00ffff; }
#azure .hover\:bg-theme-300\/20.service-card::before { background-color: #f0ffff; }
#bisque .hover\:bg-theme-300\/20.service-card::before { background-color: #ffe4c4; }
#bronze .hover\:bg-theme-300\/20.service-card::before { background-color: #cd7f32; }
#charcoal .hover\:bg-theme-300\/20.service-card::before { background-color: #36454f; }
#emerald .hover\:bg-theme-300\/20.service-card::before { background-color: #50c878; }
#ivory .hover\:bg-theme-300\/20.service-card::before { background-color: #fffff0; }
#jade .hover\:bg-theme-300\/20.service-card::before { background-color: #00a86b; }
#peach .hover\:bg-theme-300\/20.service-card::before { background-color: #ffe5b4; }
#rose .hover\:bg-theme-300\/20.service-card::before { background-color: #ff007f; }
#ruby .hover\:bg-theme-300\/20.service-card::before { background-color: #e0115f; }
#sapphire .hover\:bg-theme-300\/20.service-card::before { background-color: #0f52ba; }
#sepia .hover\:bg-theme-300\/20.service-card::before { background-color: #704214; }
#steel .hover\:bg-theme-300\/20.service-card::before { background-color: #4682b4; }
#amber .hover\:bg-theme-300\/20.service-card::before { background-color: #ffbf00; }
#cerulean .hover\:bg-theme-300\/20.service-card::before { background-color: #007ba7; }
#periwinkle .hover\:bg-theme-300\/20.service-card::before { background-color: #ccccff; }
#scarlet .hover\:bg-theme-300\/20.service-card::before { background-color: #ff2400; }
#slate .hover\:bg-theme-300\/20.service-card::before { background-color: #708090; }
@@ -0,0 +1,10 @@
---
# For configuration options and examples, please see:
# https://gethomepage.dev/configs/docker/
# my-docker:
# host: 127.0.0.1
# port: 2375
# my-docker:
# socket: /var/run/docker.sock
@@ -0,0 +1,2 @@
---
# sample kubernetes config
@@ -0,0 +1,5 @@
---
# pve:
# url: https://proxmox.host.or.ip:8006
# token: username@pam!Token ID
# secret: secret
@@ -0,0 +1,341 @@
---
######################################### HOME TAB SERVICES ######################################################################
- Infrastructure:
- Proxmox 1:
id: blue
tab: Home
href: https://192.168.2.1:8006
icon: proxmox.png
description: Virtual Server 1
widget:
type: proxmox
url: https://192.168.2.1:8006
username: api@pam!homepage
password: faca2bd5-e6df-4192-85ce-928fccde4623
node: pve
fields: ["vms", "lxc", "resources.cpu", "resources.mem"]
- Proxmox 2:
id: yellow
tab: Home
href: https://192.168.2.2:8006
icon: proxmox.png
description: Virtual Server 2
widget:
type: proxmox
url: https://192.168.2.2:8006
username: api@pam!homepage
password: 1b2fa246-a994-4f93-ab4d-1c91a4aea614
node: pve
fields: ["vms", "lxc", "resources.cpu", "resources.mem"]
- Authentik:
id: red
tab: Home
href: https://auth.mikemcfetridge.com
icon: authentik.png
description: Authentik SSO
widget:
type: authentik
url: https://192.168.2.3:9543
key: NW5ldUK8qDRY91ny67dE6ckWCMXThF9nwj6EZw7pKbS5FzXx5wouAo4sC19n
version: 2
- Dockhand:
id: cyan
tab: Home
href: http://192.168.2.3:3200
icon: dockhand.png
description: Dockhand
widget:
type: dockhand
url: http://192.168.2.3:3200
username: mmcfetridge
password: "!Sucyetat123"
- Applications:
- OwnCloud:
id: purple
tab: Home
icon: owncloud.png
href: https://owncloud.mcfetridge.us
description: File Sync & Share
- Immich:
id: white
tab: Home
href: https://immich.mikemcfetridge.com
icon: immich.png
description: Photos Server
widget:
type: immich
url: http://192.168.2.3:2283
key: "MLbcal27A67J6FZV1PIb3AdMsRf8xZC1uruOLB7js"
fields: ["users", "photos", "storage"]
version: 2
- Trilium:
id: gold
tab: Home
href: http://192.168.2.3:8180
icon: trilium.png
description: Trilium Server
widget:
type: trilium
url: http://192.168.2.3:8180
key: "Z0qLO33tMHgy_c0nq/pZAJMR/8Em5xrwOEJ6EfiyNEWkMurMSnRdVdtM="
- Forgejo:
id: green
tab: Home
href: https://forgejo.mikemcfetridge.com
icon: forgejo.png
description: Forgejo Git & CICD
- MySpeed:
id: lime
tab: Home
icon: myspeed.png
href: http://192.168.2.3:5216
widget:
type: myspeed
url: http://192.168.2.3:5216
password: "!Sucyetat123" # only required if password is set
- mealie:
id: cobalt
tab: Home
icon: mealie.png
href: http://192.168.2.7:9000/
widget:
type: mealie
url: http://192.168.2.7:9000
key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb25nX3Rva2VuIjp0cnVlLCJpZCI6IjVlYzIwNTk2LTEyY2QtNDFkMS05MTc5LWUzOWFkYjk3OWE2MyIsIm5hbWUiOiJob21lcGFnZSIsImludGVncmF0aW9uX2lkIjoiZ2VuZXJpYyIsImV4cCI6MTkzNjU0MzE3N30.C1PWK51JOpmzHwR9zmlvVShWC7sasKv0aO6PS47LQqo
version: 2 # only required if version > 1, defaults to 1
############################################## MEDIA TAB Services ###########################################################
- Media downloader:
- Calendar:
id: gray
tab: Media
name: Calendar
widget:
type: calendar
view: agenda
maxEvents: 6
firstDayInWeek: sunday
integrations:
- type: sonarr
service_group: Media downloader
service_name: Sonarr
- type: radarr
service_group: Media downloader
service_name: Radarr
- Sonarr:
id: crimson
tab: Media
href: http://192.168.2.16:8989
icon: sonarr.png
description: TV Show Service
widget:
type: sonarr
url: http://192.168.2.16:8989
key: 8746f486831545e3b6c9014657ac9c11
fields: ["wanted", "queued", "series"]
- Radarr:
id: lavender
tab: Media
href: http://192.168.2.16:7878
icon: radarr.png
description: Movie Service
widget:
type: radarr
url: http://192.168.2.16:7878
key: ee95503f6fca4cf7b9c8c3f3fc45df39
fields: ["wanted", "missing", "queued", "movies"]
- Qbittorrent:
id: teal
tab: Media
href: http://192.168.2.16:8118
icon: qbittorrent.png
description: Download Client
widget:
type: qbittorrent
url: http://192.168.2.16:8118
username: admin
password: "!Sucyetat123"
- Media Apps:
- Jellyfin:
id: violet
tab: Media
href: http://192.168.2.16:8096
icon: jellyfin.png
description: Movies & TV Shows
widget:
type: jellyfin
url: http://192.168.2.16:8096
key: 617cc65c15da4b3eaac10568c25a8c36
enableBlocks: true
enableNowPlaying: true
- Jellyseerr:
id: aqua
tab: Media
href: http://192.168.2.16:5055
icon: jellyseerr.png
description: Movie/show Database
widget:
type: jellyseerr
url: http://192.168.2.16:5055
key: MTc1NDI2NTczOTQyMDA2ODRjOWY0LWJmZmEtNGE1OC05MzM2LTAwMDM4OWRmYmQ3Yg==
fields: ["pending", "approved", "available"]
- Navidrome:
id: navy
tab: Media
icon: navidrome.png
href: http://192.168.2.16:4533
widget:
type: navidrome
url: http://192.168.2.16:4533
user: mmcfetridge
token: 05dee16046666bb05d98291b9c6e39d3
salt: 50c05a
- AudioBookShelf:
id: beige
tab: Media
href: http://192.168.2.16:13378
icon: audiobookshelf.png
description: Index Server
widget:
type: audiobookshelf
url: http://192.168.2.16:13378
key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4MDQ5Y2U4Mi0xZjlmLTRiYTEtOWY3Zi03N2U4YmRiYjg0OGEiLCJ1c2VybmFtZSI6Im1tY2ZldHJpZGdlIiwiaWF0IjoxNzQ4MTk4NzUyfQ.Tey3wxo61euuJEwc2ziE_PukdOUz7pNZKG8SmOdWB-c"
fields: ["podcasts", "books"]
##################################### Network & System TAB Services ##############################################################
- Network:
- Pi-hole:
id: slate
tab: Network
href: http://192.168.2.8/admin/login
icon: pi-hole.png
widget:
type: pihole
url: http://192.168.2.8
version: 6
key: "!Sucyetat123"
fields: ["queries", "blocked", "blocked_percent", "gravity"]
- Technitium:
id: burgundy
tab: Network
href: http://192.168.2.9:5380
icon: technitium.png
description: Technitium DNS Server
widget:
type: technitium
url: http://192.168.2.9:5380
key: 03348d06cd472fbd091d8232b1c13321ff70a8628dad7b0c23dd4512a18fb34b
range: LastDay
fields: ["totalQueries", "totalAuthoritative", "totalCached", "totalServerFailure"]
- Proxy Manager:
id: emerald
tab: Network
href: http://192.168.2.3:81
icon: nginx-proxy-manager.png
description: Nginx Proxy Manager
widget:
type: npm
url: http://192.168.2.3:81
username: mmcfetridg@aol.com
password: "!Sucyetat123"
fields: ["enabled", "disabled", "total"]
- Pangolin:
id: salmon
tab: Network
icon: pangolin.png
href: https://pangolin.mmcfetridge.net
description: External network
widget:
type: pangolin
url: https://api.mmcfetridge.net/v1
key: "2o4g9gak61mig2i.5s5yxu4o2ek7haepzumar2ldl4mmzqpbrcfnpqdf"
org: homelab
- Systems:
- Gluetun:
id: jade
tab: System
href: http://192.168.2.16:8000
icon: gluetun.png
description: Media VPN Server
widget:
type: gluetun
url: http://192.168.2.16:8000
fields: ["public_ip", "region", "country"]
- Public IP:
icon: mdi-web
href: https://whatismyipaddress.com/
widget:
type: customapi
url: https://api.ipify.org?format=json
refreshInterval: 3600000 # Refreshes once an hour (in milliseconds)
method: GET
mappings:
- field: ip
label: WAN IP
format: text
############################################## Bookmark TAB Services #############################################################
- Bookmarks:
- Github:
id: indigo
tab: Bookmarks
abbr: GH
href: https://github.com
icon: github-light.png
description: GitHub
- Linkwarden:
id: charcoal
tab: Bookmarks
href: https://lw.mikemcfetridge.com
icon: linkwarden.png
description: Web Bookmarker
widget:
type: linkwarden
url: http://192.168.2.12:3000
key: "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..ZLJgig_ol-n3i1tv.vhWCnMSHVd81bdidL85-imXKpxGC0U3mHlvmkQyIJqJvcFRRJVBAasw9C0cmfSXVPq08HT-B-3zUAmftnPRymPHMJCkfo8pv_XBbEPneIFH-n59_dg26.qcXSK2REP3M_Vdgp0aOqcA"
fields: ["links", "collections"]
- Semaphore:
id: cerulean
tab: Bookmarks
icon: semaphore.png
href: http://192.168.2.11:3000
description: CI/CD Automation
- Wg-Easy:
id: rose
tab: Bookmarks
icon: wireguard.png
href: http://192.168.2.3:10086
description: Wireguard VPN
- YouTube:
id: coral
tab: Bookmarks
icon: youtube.png
href: https://youtube.com/
description: Video Platform
@@ -0,0 +1,55 @@
---
title: Miker's Homepage
description: Here is my awesome homepage
background:
image: https://backiee.com/static/wallpapers/1000x563/412861.webp
blur: sm
saturate: 50
brightness: 75
opacity: 75
theme: dark
color: slate
layout:
Infrastructure:
icon: mdi-folder-network
tab: Home
header: true
columns: 3
Applications:
icon: mdi-application
tab: Home
header: true
columns: 3
Media downloader:
icon: mdi-multimedia
tab: Media
header: true
columns: 3
Media Apps:
icon: mdi-movie
tab: Media
header: true
columns: 3
Network:
tab: System
icon: mdi-switch
header: true
columns: 3
Systems:
tab: System
icon: mdi-television
header: true
columns: 3
Bookmarks:
tab: Bookmarks
icon: mdi-baby
style: row
columns: 4
fiveColumns: true
useEqualHeights: true
showStats: true
@@ -0,0 +1,42 @@
# Homepage Widgets Configuration
# Organized by System, Environment, and Search
- logo:
icon: wireguard.svg # optional
- resources:
cpu: true
memory: true
disk: /
cputemp: true
tempmin: 0
tempmax: 100
uptime: true
units: imperial
refresh: 3000
diskUnits: bytes
network: true
- openmeteo:
label: Bealeton
latitude: 38.5718
longitude: -77.7639
timezone: America/New_York
units: imperial
cache: 5
format:
maximumFractionDigits: 1
- datetime:
text_size: sm
format:
dateStyle: short
timeStyle: short
hour12: true
timeZone: 'America/New_York'
- search:
provider: custom
url: http://localhost:8080/search?q=
focus: true
target: _blank
showSearchSuggestions: true
@@ -0,0 +1,44 @@
services:
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
environment:
HOMEPAGE_ALLOWED_HOSTS: "*" # required, may need port. See gethomepage.dev/installation/#homepage_allowed_hosts
PUID: ${PUID} # Reference PUID from .env
PGID: ${PGID} # Reference PGID from .env
DOCKER_HOST: tcp://socket-proxy:2375
ports:
- 3000:3000
env_file:
- .env # Link to the .env file
volumes:
- ./config:/app/config # Make sure your local config directory exists
- ./images:/app/public/images
- /var/run/docker.sock:/var/run/docker.sock:ro # optional, for docker integrations
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
dockerproxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
container_name: dockerproxy
environment:
- CONTAINERS=1 # Allow access to viewing containers
- SERVICES=1 # Allow access to viewing services (necessary when using Docker Swarm)
- TASKS=1 # Allow access to viewing tasks (necessary when using Docker Swarm)
- POST=0 # Disallow any POST operations (effectively read-only)
ports:
- 127.0.0.1:2375:2375
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # Mounted as read-only
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:2375"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

+5
View File
@@ -0,0 +1,5 @@
HOMARR_PORT=7575
PUID=1000
PGID=1000
# TZ for container timezone
TZ=America/New_York
+30
View File
@@ -0,0 +1,30 @@
services:
# Homarr - Application Dashboard
homarr:
image: ghcr.io/ajnart/homarr:latest
container_name: homarr
security_opt:
- no-new-privileges:true
restart: unless-stopped
env_file:
- .env
networks:
- default
- socket_proxy
ports:
- "$HOMARR_PORT:7575"
volumes:
# - /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration
- ./appdata/homarr/configs:/app/data/configs
- ./appdata/homarr/icons:/app/public/icons
- ./appdata/homarr/data:/data
environment:
- DOCKER_HOST
PUID: ${PUID} # Reference PUID from .env
PGID: ${PGID} # # DOCKER-LABELS-PLACEHOLDER
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7575"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
@@ -0,0 +1,40 @@
services:
arcane:
image: ghcr.io/ofkm/arcane:latest
container_name: arcane
ports:
- '3552:3552'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/miker/docker/arcane/data:/app/data
- /home/miker/docker/arcane/projects:/app/data/projects
environment:
- APP_URL=http://192.168.2.21:3552
- PUID=1000
- PGID=1000
- ENCRYPTION_KEY=OjYxMUkyZVdqYjd1QWVZbw==FUwh8qzl
- JWT_SECRET=FIw1uz3TbDH7U9y
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3552"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
# Default creds: arcane / arcane-admin
# arcane agent Install
services:
arcane-agent:
image: ghcr.io/getarcaneapp/arcane-headless:latest
container_name: arcane-agent
ports:
- '3553:3553'
environment:
- AGENT_MODE=true
# Use a strong, temporary bootstrap token for pairing:
- AGENT_BOOTSTRAP_TOKEN=FIw1uz3TbDH7U9y
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
restart: unless-stopped
+11
View File
@@ -0,0 +1,11 @@
ADMIN_USERNAME=admin
ADMIN_PASSWORD=super_secret_password_change_me
# Base Paths
DOCKER_BASE_PATH=/home/miker/docker
# Ports
DOCKHAND_PORT=3200
# Container Names
PROXY_NAME=docker-socket-proxy
DOCKHAND_NAME=dock-hand
@@ -0,0 +1,46 @@
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: ${PROXY_NAME}
restart: unless-stopped
environment:
# These flags allow only the actions Dockhand needs
- CONTAINERS=1
- IMAGES=1
- NETWORKS=1
- VOLUMES=1
- INFO=1
# Disable anything not strictly necessary
- POST=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
dockhand:
image: fnsys/dockhand:latest
container_name: ${DOCKHAND_NAME}
restart: unless-stopped
ports:
- 3200:3000
volumes:
- ${DOCKER_BASE_PATH}/dockhand:/app/data
- ${DOCKER_BASE_PATH}/dockhand/stack:/stack
- ${DOCKER_BASE_PATH}:/DockerConfigs
environment:
# Point Dockhand to the proxy instead of the local socket file
- DOCKER_HOST=tcp://socket-proxy:2375
- ADMIN_USERNAME=${ADMIN_USERNAME}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- TZ=UTC
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
networks:
- management-net
depends_on:
- socket-proxy
networks:
management-net:
driver: bridge
@@ -0,0 +1,13 @@
services:
hawser:
image: ghcr.io/finsys/hawser:latest
container_name: hawser_agent
ports:
- "2376:2376"
environment:
- DOCKER_SOCKET=/var/run/docker.sock
- TOKEN=sj8GwEiPz7hncCpZB69P01ValE9ayXpGwTY5AYQFYxo
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/miker/docker/dockhand/stack:/stack
restart: unless-stopped
@@ -0,0 +1,19 @@
services:
periphery:
image: ghcr.io/moghtech/komodo-periphery:latest # Or a specific version
container_name: komodo-periphery
ports:
- "8120:8120" # Expose Periphery API port
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Required to interact with Docker daemon
- komodo-periphery-data:/app/data # Persistent storage for periphery data
environment:
# Optional: Set a specific log level (e.g., info, debug, error)
- LOG_LEVEL=info
- KOMODO_CORE_ADDRESS=192.168.2.7 # Replace with your Komodo Core IP
- KOMODO_CORE_PASSKEY=HModV9yPmOBD4P # Replace with your Komodo Core passkey
- PERIPHERY_SSL_ENABLED=true
- PERIPHERY_DISABLE_TERMINALS=false
restart: unless-stopped
volumes:
komodo-periphery-data:
+142
View File
@@ -0,0 +1,142 @@
####################################
# 🦎 KOMODO COMPOSE - VARIABLES 🦎 #
####################################
## These compose variables can be used with all Komodo deployment options.
## Pass these variables to the compose up command using `--env-file komodo/compose.env`.
## Additionally, they are passed to both Komodo Core and Komodo Periphery with `env_file: ./compose.env`,
## so you can pass any additional environment variables to Core / Periphery directly in this file as well.
## Stick to a specific version, or use `latest`
COMPOSE_KOMODO_IMAGE_TAG=latest
## DB credentials
KOMODO_DB_USERNAME=admin
KOMODO_DB_PASSWORD=PCjFCsX0yC0G44
## Configure a secure passkey to authenticate between Core / Periphery.
KOMODO_PASSKEY=HModV9yPmOBD4P
## Set your time zone for schedules
## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=America/New_York
#=-------------------------=#
#= Komodo Core Environment =#
#=-------------------------=#
## Full variable list + descriptions are available here:
## 🦎 https://github.com/moghtech/komodo/blob/main/config/core.config.toml 🦎
## Note. Secret variables also support `${VARIABLE}_FILE` syntax to pass docker compose secrets.
## Docs: https://docs.docker.com/compose/how-tos/use-secrets/#examples
## Used for Oauth / Webhook url suggestion / Caddy reverse proxy.
KOMODO_HOST=https://komodo.mikemcfetridge.com
## Displayed in the browser tab.
KOMODO_TITLE=Komodo
## Create a server matching this address as the "first server".
## Use `https://host.docker.internal:8120` when using systemd-managed Periphery.
KOMODO_FIRST_SERVER=https://192.168.2.21:8120
## Make all buttons just double-click, rather than the full confirmation dialog.
KOMODO_DISABLE_CONFIRM_DIALOG=true
## Rate Komodo polls your servers for
## status / container status / system stats / alerting.
## Options: 1-sec, 5-sec, 15-sec, 1-min, 5-min, 15-min
## Default: 15-sec
KOMODO_MONITORING_INTERVAL="15-sec"
## Interval at which to poll Resources for any updates / automated actions.
## Options: 15-min, 1-hr, 2-hr, 6-hr, 12-hr, 1-day
## Default: 1-hr
KOMODO_RESOURCE_POLL_INTERVAL="15-min"
## Used to auth incoming webhooks. Alt: KOMODO_WEBHOOK_SECRET_FILE
KOMODO_WEBHOOK_SECRET=oop28TaGucFUTh
## Used to generate jwt. Alt: KOMODO_JWT_SECRET_FILE
KOMODO_JWT_SECRET=rm4O312hzsl6Pt
## Time to live for jwt tokens.
## Options: 1-hr, 12-hr, 1-day, 3-day, 1-wk, 2-wk
KOMODO_JWT_TTL="1-day"
## Enable login with username + password.
KOMODO_LOCAL_AUTH=true
## Disable new user signups.
KOMODO_DISABLE_USER_REGISTRATION=true
## All new logins are auto enabled
KOMODO_ENABLE_NEW_USERS=false
## Disable non-admins from creating new resources.
KOMODO_DISABLE_NON_ADMIN_CREATE=false
## Allows all users to have Read level access to all resources.
KOMODO_TRANSPARENT_MODE=false
## Prettier logging with empty lines between logs
KOMODO_LOGGING_PRETTY=false
## More human readable logging of startup config (multi-line)
KOMODO_PRETTY_STARTUP_CONFIG=false
## OIDC Login
KOMODO_OIDC_ENABLED=false
## Must reachable from Komodo Core container
# KOMODO_OIDC_PROVIDER=https://oidc.provider.internal/application/o/komodo
## Change the host to one reachable be reachable by users (optional if it is the same as above).
## DO NOT include the `path` part of the URL.
# KOMODO_OIDC_REDIRECT_HOST=https://oidc.provider.external
## Your OIDC client id
# KOMODO_OIDC_CLIENT_ID= # Alt: KOMODO_OIDC_CLIENT_ID_FILE
## Your OIDC client secret.
## If your provider supports PKCE flow, this can be ommitted.
# KOMODO_OIDC_CLIENT_SECRET= # Alt: KOMODO_OIDC_CLIENT_SECRET_FILE
## Make usernames the full email.
## Note. This does not work for all OIDC providers.
# KOMODO_OIDC_USE_FULL_EMAIL=true
## Add additional trusted audiences for token claims verification.
## Supports comma separated list, and passing with _FILE (for compose secrets).
# KOMODO_OIDC_ADDITIONAL_AUDIENCES=abc,123 # Alt: KOMODO_OIDC_ADDITIONAL_AUDIENCES_FILE
## Github Oauth
KOMODO_GITHUB_OAUTH_ENABLED=false
# KOMODO_GITHUB_OAUTH_ID= # Alt: KOMODO_GITHUB_OAUTH_ID_FILE
# KOMODO_GITHUB_OAUTH_SECRET= # Alt: KOMODO_GITHUB_OAUTH_SECRET_FILE
## Google Oauth
KOMODO_GOOGLE_OAUTH_ENABLED=false
# KOMODO_GOOGLE_OAUTH_ID= # Alt: KOMODO_GOOGLE_OAUTH_ID_FILE
# KOMODO_GOOGLE_OAUTH_SECRET= # Alt: KOMODO_GOOGLE_OAUTH_SECRET_FILE
## Aws - Used to launch Builder instances.
KOMODO_AWS_ACCESS_KEY_ID= # Alt: KOMODO_AWS_ACCESS_KEY_ID_FILE
KOMODO_AWS_SECRET_ACCESS_KEY= # Alt: KOMODO_AWS_SECRET_ACCESS_KEY_FILE
#=------------------------------=#
#= Komodo Periphery Environment =#
#=------------------------------=#
## Full variable list + descriptions are available here:
## 🦎 https://github.com/moghtech/komodo/blob/main/config/periphery.config.toml 🦎
## Specify the root directory used by Periphery agent.
PERIPHERY_ROOT_DIRECTORY=/etc/komodo
## Periphery passkeys must include KOMODO_PASSKEY to authenticate.
PERIPHERY_PASSKEYS=${KOMODO_PASSKEY}
## Specify whether to disable the terminals feature
## and disallow remote shell access (inside the Periphery container).
PERIPHERY_DISABLE_TERMINALS=false
## Enable SSL using self signed certificates.
## Connect to Periphery at https://address:8120.
PERIPHERY_SSL_ENABLED=true
## If the disk size is overreporting, can use one of these to
## whitelist / blacklist the disks to filter them, whichever is easier.
## Accepts comma separated list of paths.
## Usually whitelisting just /etc/hostname gives correct size.
PERIPHERY_INCLUDE_DISK_MOUNTS=/etc/hostname
# PERIPHERY_EXCLUDE_DISK_MOUNTS=/snap,/etc/repos
## Prettier logging with empty lines between logs
PERIPHERY_LOGGING_PRETTY=false
## More human readable logging of startup config (multi-line)
PERIPHERY_PRETTY_STARTUP_CONFIG=false
@@ -0,0 +1,106 @@
###################################
# 🦎 KOMODO COMPOSE - FERRETDB 🦎 #
###################################
## This compose file will deploy:
## 1. Postgres + FerretDB Mongo adapter (https://www.ferretdb.com)
## 2. Komodo Core
## 3. Komodo Periphery
services:
postgres:
# Recommended: Pin to a specific version
# https://github.com/FerretDB/documentdb/pkgs/container/postgres-documentdb
image: ghcr.io/ferretdb/postgres-documentdb
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
restart: unless-stopped
# ports:
# - 5432:5432
env_file: ./compose.env
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: PCjFCsX0yC0G44
POSTGRES_DB: postgres
ferretdb:
# Recommended: Pin to a specific version
# https://github.com/FerretDB/FerretDB/pkgs/container/ferretdb
image: ghcr.io/ferretdb/ferretdb
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
restart: unless-stopped
depends_on:
- postgres
# ports:
# - 27017:27017
env_file: ./compose.env
volumes:
- ferretdb-state:/state
environment:
FERRETDB_POSTGRESQL_URL: postgres://admin:PCjFCsX0yC0G44@postgres:5432/postgres
core:
image: ghcr.io/moghtech/komodo-core:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
restart: unless-stopped
depends_on:
- ferretdb
ports:
- 9120:9120
env_file: ./compose.env
environment:
KOMODO_DATABASE_ADDRESS: ferretdb:27017
KOMODO_DATABASE_USERNAME: admin
KOMODO_DATABASE_PASSWORD: PCjFCsX0yC0G44
volumes:
## Core cache for repos for latest commit hash / contents
- repo-cache:/repo-cache
## Store sync files on server
# - /path/to/syncs:/syncs
## Optionally mount a custom core.config.toml
# - /path/to/core.config.toml:/config/config.toml
## Allows for systemd Periphery connection at
## "http://host.docker.internal:8120"
# extra_hosts:
# - host.docker.internal:host-gateway
## Deploy Periphery container using this block,
## or deploy the Periphery binary with systemd using
## https://github.com/moghtech/komodo/tree/main/scripts
periphery:
image: ghcr.io/moghtech/komodo-periphery:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
labels:
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
restart: unless-stopped
env_file: ./compose.env
# Need to make sure you add this or deploying agents will be a pain.
ports:
- 8120:8120
volumes:
## Mount external docker socket
- /var/run/docker.sock:/var/run/docker.sock
## Allow Periphery to see processes outside of container
- /proc:/proc
## Specify the Periphery agent root directory.
## Must be the same inside and outside the container,
## or docker will get confused. See https://github.com/moghtech/komodo/discussions/180.
## Default: /etc/komodo.
- ${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}:${PERIPHERY_ROOT_DIRECTORY:-/etc/komodo}
volumes:
# Postgres
postgres-data: # FerretDB
ferretdb-state: # Core
repo-cache:
# To start up - Run
# docker compose -p komodo -f komodo/ferretdb.compose.yaml --env-file komodo/compose.env up -d
# To stop up - Run
# docker compose -p komodo -f komodo/ferretdb.compose.yaml --env-file komodo/compose.env up -d
@@ -0,0 +1,13 @@
---
services:
app:
container_name: portainer-agent
deploy:
mode: global
image: docker.io/portainer/agent:latest
ports:
- 9001:9001
volumes:
- /run/docker.sock:/var/run/docker.sock
- /var/lib/docker/volumes:/var/lib/docker/volumes
restart: unless-stopped
@@ -0,0 +1,19 @@
---
services:
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
ports:
- 9445:9443
volumes:
- ./data:/data
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
# Network to join in docker which is named below
networks:
- proxy
# create PROXY network for NPM security. This will allow NPM to see and talk to >
networks:
proxy:
external: true
@@ -0,0 +1,22 @@
services:
# Portainer - WebUI for Containers
portainer:
container_name: portainer
image: portainer/portainer-ce:latest
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["core", "all"]
networks:
- default
- socket_proxy
# command: -H unix:///var/run/docker.sock # Use Docker Socket Proxy instead for improved security
command: -H tcp://socket-proxy:2375
ports:
- "$PORTAINER_PORT:9000"
volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro # Use Docker Socket Proxy instead for improved security
- $DOCKERDIR/appdata/portainer/data:/data
environment:
- TZ=$TZ
# DOCKER-LABELS-PLACEHOLDER
@@ -0,0 +1,31 @@
services:
semaphore:
ports:
- 3300:3300
image: semaphoreui/semaphore:latest
container_name: semaphore
environment:
SEMAPHORE_DB_DIALECT: sqlite
SEMAPHORE_ADMIN: mmcfetridge
SEMAPHORE_ADMIN_PASSWORD: Dy7zxAyDqdYN443g4pI3
SEMAPHORE_ADMIN_NAME: Mike McFetridge
SEMAPHORE_ADMIN_EMAIL: mmcfetridg@aol.com
SEMAPHORE_PORT: "3300"
ANSIBLE_HOST_KEY_CHECKING: "False"
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "mrmKv7EztqRCnlGo34IvlvoilXqvDKYnFIWX2lg+hZc="
volumes:
- semaphore_data:/var/lib/semaphore
- semaphore_config:/etc/semaphore
- semaphore_tmp:/tmp/semaphore
networks:
- semaphore-net
volumes:
semaphore_data:
semaphore_config:
semaphore_tmp:
networks:
semaphore-net:
driver: bridge
@@ -0,0 +1,22 @@
URL for the task template
ansible/update/update-apt-packages.yaml
Need to create two files:
In order to ensure Semaphore can run commands properly, well need a line similar to the one below added to /etc/sudoers.d/semaphore on the target (the instance that our Semaphore server will be configuring). Be sure to replace semaphore in both the file name and inside the file to match the username of the user you intend to use with Semaphore.
semaphore file
semaphore ALL=(ALL) NOPASSWD: ALL
miker file
miker ALL=(ALL) NOPASSWD: ALL
Need to be logged into Ubuntu03, go to the .ssh directory and run the following command to copy over the rsa.pub file.
scp id_rsa.pub miker@192.168.0.181:/home/miker/.ssh
or
ssh-copy-id miker@192.168.0.185
@@ -0,0 +1,15 @@
services:
agent:
container_name: tugtainer-agent
image: quenary/tugtainer-agent:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
environment:
# The list of available variables is in env.example
AGENT_SECRET: home-mikemcfetridge-com
read_only: true
tmpfs:
- /run
ports:
- '8001:8001'
@@ -0,0 +1,12 @@
services:
app:
container_name: tugtainer
image: quenary/tugtainer:latest
volumes:
- tugtainer_data:/tugtainer
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
ports:
- '9412:80'
volumes:
tugtainer_data:
+17
View File
@@ -0,0 +1,17 @@
TZ=America/New_York
WATCHTOWER_CLEANUP=true
WATCHTOWER_INCLUDE_RESTARTING=true
WATCHTOWER_ROLLING_RESTARTING=true
WATCHTOWER_SCHEDULE=0 0 4 * * *
WATCHTOWER_INCLUDE_STOPPED=true
WATCHTOWER_NOTIFICATIONS=email
WATCHTOWER_NOTIFICATIONS_HOSTNAME=Docker-Deply # Add Server Name or IP Address
WATCHTOWER_NOTIFICATION_EMAIL_FROM=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_TO=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER=mail.mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=465
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=!Sucyetat123
WATCHTOWER_NOTIFICATION_EMAIL_DELAY=2
PUID=1000
PGID=1000
@@ -0,0 +1,31 @@
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
env_file: .env
environment:
- DOCKER_API_VERSION=1.44
- TZ=${TZ}
- PUID:=${PUID} # Reference PUID from .env
- PGID=${PGID} # Reference PGID from .env
- WATCHTOWER_CLEANUP=${WATCHTOWER_CLEANUP}
- WATCHTOWER_INCLUDE_RESTARTING=${WATCHTOWER_INCLUDE_RESTARTING}
- WATCHTOWER_ROLLING_RESTARTING=${WATCHTOWER_ROLLING_RESTARTING}
- WATCHTOWER_SCHEDULE=${WATCHTOWER_SCHEDULE}
- WATCHTOWER_INCLUDE_STOPPED=${WATCHTOWER_INCLUDE_STOPPED}
# - WATCHTOWER_NOTIFICATIONS=${WATCHTOWER_NOTIFICATIONS}
# - WATCHTOWER_NOTIFICATIONS_HOSTNAME=${WATCHTOWER_NOTIFICATIONS_HOSTNAME}
# - WATCHTOWER_NOTIFICATION_EMAIL_FROM=${WATCHTOWER_NOTIFICATION_EMAIL_FROM}
# - WATCHTOWER_NOTIFICATION_EMAIL_TO=${WATCHTOWER_NOTIFICATION_EMAIL_TO}
# - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER}
# - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT}
# - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER}
# - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD}
security_opt:
- no-new-privileges:true
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
@@ -0,0 +1,16 @@
services:
# DeUnhealth - Restart your unhealthy containers safely (e.g. containers depending on VPN and VPN reconnects)
deunhealth:
image: qmcgaw/deunhealth
container_name: deunhealth
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "all"]
networks:
- socket_proxy
environment:
- LOG_LEVEL=info
- HEALTH_SERVER_ADDRESS=127.0.0.1:9999
- TZ=$TZ
- DOCKER_HOST
@@ -0,0 +1,25 @@
services:
# qBittorrent - Torrent downloader
qbittorrent-vpn:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent-vpn
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "downloads", "all"]
network_mode: "service:gluetun"
volumes:
- $DOCKERDIR/appdata/qbittorrent-vpn:/config
- $DOWNLOADSDIR:/data/downloads # Ensure that downloads folder is set to /data/downloads in qBittorrent
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
UMASK_SET: 002
healthcheck: # https://github.com/qdm12/gluetun/issues/641#issuecomment-933856220
test: "curl -sf https://example.com || exit 1"
interval: 1m
timeout: 10s
retries: 1
labels: # Traefik labels added via file provider app-qbittorrent.yml in rules folder
- "deunhealth.restart.on.unhealthy=true"
@@ -0,0 +1,22 @@
services:
# qBittorrent - Torrent downloader
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "downloads", "all"]
networks:
- default
ports:
- "$QBITTORRENT_PORT:8080"
volumes:
- $DOCKERDIR/appdata/qbittorrent:/config
- $DOWNLOADSDIR:/data/downloads # Ensure that downloads folder is set to /data/downloads in qBittorrent
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
UMASK_SET: 002
# DOCKER-LABELS-PLACEHOLDER
@@ -0,0 +1,32 @@
services:
nextcloud-aio-mastercontainer:
image: nextcloud/all-in-one:latest
init: true
sig-proxy: false
restart: always
container_name: nextcloud-aio-mastercontainer
volumes:
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 8080:8080
environment:
- APACHE_PORT=11000
- APACHE_IP_BINDING=0.0.0.0
- APACHE_ADDITIONAL_NETWORK=""
- SKIP_DOMAIN_VALIDATION=true
volumes:
nextcloud_aio_mastercontainer:
name: nextcloud_aio_mastercontainer # This line is not allowed to be changed.
# My Passphase
# resigned lubricant petty eternal gumball coroner stream nervy
# admin
$ f34920a6ecaa8cb825d6c46972841250e4ec0fa68c46f64f
# Backup information
# This is your encryption password for backups: f5b3a3dc742ac7b354d3e2d3926d8bce9fb50bcd1b75afc4
@@ -0,0 +1,18 @@
---
services:
duplicati:
image: lscr.io/linuxserver/duplicati:latest
container_name: duplicati
environment:
- PUID=1000 # Your host user ID
- PGID=1000 # Your host group ID
- TZ=America/New_York
- SETTINGS_ENCRYPTION_KEY=84Jo17Ecw5muzGgqVjpeZQYnWBH7XFM7R01CdR646jSIfNbxgp # <-- Add this line
volumes:
- /home/miker/docker/duplicati/config:/config
- /home/miker/docker/immich/library:/source/photos:ro # Mounts your photo directory as read-only
- /home/miker/docker:/source/apps:ro # Mounts your application data files
- /home/miker/docker/duplicati/backups:/backups # Optional local backup location
ports:
- 8250:8200
restart: unless-stopped
+6
View File
@@ -0,0 +1,6 @@
OWNCLOUD_VERSION=latest
OWNCLOUD_DOMAIN=129.213.86.62:8080
OWNCLOUD_TRUSTED_DOMAINS=129.213.86.62,owncloud.mcfetridge.us,localhost
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
HTTP_PORT=8080
@@ -0,0 +1,72 @@
version: "3"
volumes:
files:
driver: local
mysql:
driver: local
redis:
driver: local
services:
owncloud:
image: owncloud/server:${OWNCLOUD_VERSION}
container_name: owncloud_server
restart: always
ports:
- ${HTTP_PORT}:8080
depends_on:
- mariadb
- redis
environment:
- OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
- OWNCLOUD_TRUSTED_DOMAINS=${OWNCLOUD_TRUSTED_DOMAINS}
- OWNCLOUD_DB_TYPE=mysql
- OWNCLOUD_DB_NAME=owncloud
- OWNCLOUD_DB_USERNAME=owncloud
- OWNCLOUD_DB_PASSWORD=owncloud
- OWNCLOUD_DB_HOST=mariadb
- OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
- OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
- OWNCLOUD_MYSQL_UTF8MB4=true
- OWNCLOUD_REDIS_ENABLED=true
- OWNCLOUD_REDIS_HOST=redis
healthcheck:
test: ["CMD", "/usr/bin/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- files:/mnt/data
mariadb:
image: mariadb:10.11 # minimum required ownCloud version is 10.9
container_name: owncloud_mariadb
restart: always
environment:
- MYSQL_ROOT_PASSWORD=owncloud
- MYSQL_USER=owncloud
- MYSQL_PASSWORD=owncloud
- MYSQL_DATABASE=owncloud
- MARIADB_AUTO_UPGRADE=1
command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=owncloud"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- mysql:/var/lib/mysql
redis:
image: redis:6
container_name: owncloud_redis
restart: always
command: ["--databases", "1"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- redis:/data
+150
View File
@@ -0,0 +1,150 @@
# ============================================================
# Tom Spark's ARR Stack — Environment Configuration
# https://github.com/loponai/arrstack
#
# INSTRUCTIONS:
# 1. Copy this file: cp .env.example .env
# 2. Fill in your VPN credentials below
# 3. Adjust timezone and user IDs if needed
# 4. Run: docker compose up -d
# ============================================================
# ============================================================
# SYSTEM SETTINGS
# ============================================================
# Your timezone (list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
TZ=America/New_York
# Your Linux user/group ID. Find yours with: id
# Most systems default to 1000. If yours is different, change these.
PUID=1000
PGID=1000
# ============================================================
# VPN SETTINGS — Pick your provider and fill in credentials
# Full provider list: https://github.com/qdm12/gluetun-wiki/tree/main/setup/providers
#
# IMPORTANT: VPN credentials are NOT your login email/password!
# You need service credentials from your VPN provider's manual
# setup or API section. See the provider examples at the bottom of this file.
# ============================================================
# Your VPN provider (surfshark, nordvpn, protonvpn, airvpn, mullvad, private internet access, windscribe, etc.)
VPN_SERVICE_PROVIDER=airvpn
# Protocol: wireguard (recommended, faster) or openvpn
VPN_TYPE=wireguard
# --- WIREGUARD CREDENTIALS ---
# For Surfshark: go to https://my.surfshark.com/vpn/manual-setup/main → WireGuard
# For other providers: see the examples at the bottom of this file
WIREGUARD_PRIVATE_KEY=eDgf3GPFy2ltgx2RkD/Vx5wKZ4dVi28YbQmSJOrbWmk=
WIREGUARD_ADDRESSES=10.175.23.30
# Only needed for some providers (AirVPN). Leave blank if not required.
# WIREGUARD_PUBLIC_KEY=
WIREGUARD_PRESHARED_KEY=+0+hrSdzRUxKDNk1Q37PNmNJ2jsj2EzF45JtbZad4lI=
# --- OPENVPN CREDENTIALS ---
# Only needed if VPN_TYPE=openvpn. Leave blank if using WireGuard.
# OPENVPN_USER=
# OPENVPN_PASSWORD=
# --- SERVER SELECTION ---
# Pick a country close to you for best speeds
SERVER_COUNTRIES=Canada
# --- PORT FORWARDING ---
# Supported by: ProtonVPN, AirVPN, PIA. Can help with upload speeds and seeding.
# Not required for downloading. Most users don't need this.
# Set to "on" if your provider supports it, leave blank otherwise (Surfshark, NordVPN, etc.).
#VPN_PORT_FORWARDING=
# If your provider requires manually specifying a port (e.g. AirVPN):
FIREWALL_VPN_INPUT_PORTS=29261
# ============================================================
# NETWORK — Static IPs for each service
# You shouldn't need to change these unless you have a conflict.
# ============================================================
IP_GLUETUN=172.39.0.2
IP_RADARR=172.39.0.3
IP_SONARR=172.39.0.4
IP_LIDARR=172.39.0.5
IP_BAZARR=172.39.0.6
IP_JELLYFIN=172.39.0.7
IP_SEERR=172.39.0.8
IP_AUDIO=172.39.0.9
IP_NAVI=172.39.0.10
IP_LIST=172.39.0.11
IP_WATCH=172.39.0.12
# ============================================================
# PROVIDER-SPECIFIC EXAMPLES
# Uncomment and fill in the section for your VPN provider.
# ============================================================
# --- NORDVPN ---
# 1. Go to: https://my.nordaccount.com/dashboard/nordvpn/manual-configuration/
# 2. Generate a WireGuard private key (NordLynx)
# 3. Paste the private key below
# VPN_SERVICE_PROVIDER=nordvpn
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_nordvpn_private_key_here
# WIREGUARD_ADDRESSES=10.5.0.2/16
# SERVER_COUNTRIES=United States
# --- PROTONVPN ---
# 1. Go to: https://account.protonvpn.com/ → Downloads → WireGuard configuration
# 2. Generate a config, open the file, copy the PrivateKey and Address
# 3. Port forwarding is supported on paid plans
# VPN_SERVICE_PROVIDER=protonvpn
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_proton_private_key_here
# WIREGUARD_ADDRESSES=10.2.0.2/32
# SERVER_COUNTRIES=United States
# VPN_PORT_FORWARDING=on
# --- SURFSHARK ---
# 1. Go to: https://my.surfshark.com/vpn/manual-setup/main
# 2. Get WireGuard credentials
# VPN_SERVICE_PROVIDER=surfshark
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_surfshark_private_key_here
# WIREGUARD_ADDRESSES=10.14.0.2/16
# SERVER_COUNTRIES=United States
# --- AIRVPN ---
# 1. Go to: https://airvpn.org/ → Client Area → Config Generator
# 2. Select Linux → WireGuard → pick a server → Generate
# 3. Copy all keys and the assigned IP
# VPN_SERVICE_PROVIDER=airvpn
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_airvpn_private_key_here
# WIREGUARD_PUBLIC_KEY=your_airvpn_public_key_here
# WIREGUARD_PRESHARED_KEY=your_airvpn_preshared_key_here
# WIREGUARD_ADDRESSES=your_assigned_ip/32
# FIREWALL_VPN_INPUT_PORTS=your_forwarded_port
# VPN_PORT_FORWARDING=on
# --- MULLVAD ---
# 1. Go to: https://mullvad.net/en/account → WireGuard configuration
# VPN_SERVICE_PROVIDER=mullvad
# VPN_TYPE=wireguard
# WIREGUARD_PRIVATE_KEY=your_mullvad_private_key_here
# WIREGUARD_ADDRESSES=your_assigned_ip/32
# SERVER_COUNTRIES=United States
# --- Watchtower ---
WATCHTOWER_CLEANUP=true
WATCHTOWER_INCLUDE_RESTARTING=true
WATCHTOWER_ROLLING_RESTARTING=true
WATCHTOWER_SCHEDULE=0 0 4 * * *
WATCHTOWER_INCLUDE_STOPPED=true
WATCHTOWER_NOTIFICATIONS=email
WATCHTOWER_NOTIFICATIONS_HOSTNAME=Docker-Deply # Add Server Name or IP Address
WATCHTOWER_NOTIFICATION_EMAIL_FROM=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_TO=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER=mail.mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=465
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=miker@mmcfetridge.net
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=!Sucyetat123
WATCHTOWER_NOTIFICATION_EMAIL_DELAY=2
PUID=1000
PGID=1000
@@ -0,0 +1,440 @@
# Tom Spark's ARR Stack — Automated Media Server
# https://github.com/loponai/arrstack
#
# Usage:
# 1. Copy .env.example to .env and fill in your VPN credentials
# 2. Run: bash setup-folders.sh
# 3. Run: docker compose up -d
#
# All VPN-protected services (qBittorrent, Prowlarr, FlareSolverr) run
# through Gluetun. If the VPN drops, traffic stops. Zero leaks.
#
# Radarr, Sonarr, Lidarr, Bazarr, Jellyfin, and Seerr do NOT run through
# the VPN — they need direct network access for speed and local connectivity.
networks:
arrnetwork:
name: arrnetwork
ipam:
config:
- subnet: 172.39.0.0/24
services:
# ============================================================
# GLUETUN — VPN Container (kill switch + tunnel)
# All VPN-protected services route through this container.
# Ports for those services are mapped HERE, not on the services themselves.
# Docs: https://github.com/qdm12/gluetun-wiki
# ============================================================
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
networks:
arrnetwork:
ipv4_address: ${IP_GLUETUN}
ports:
- 8000:8000 # Gluetun Control Server
- 8080:8080 # qBittorrent WebUI
- 6881:6881 # qBittorrent torrenting port
- 6881:6881/udp
- 9696:9696 # Prowlarr
- 8191:8191 # FlareSolverr
volumes:
- gluetun_volume:/gluetun
environment:
- VPN_SERVICE_PROVIDER=${VPN_SERVICE_PROVIDER}
- VPN_TYPE=${VPN_TYPE}
# --- WireGuard credentials (most providers) ---
- WIREGUARD_PRIVATE_KEY=${WIREGUARD_PRIVATE_KEY}
- WIREGUARD_ADDRESSES=${WIREGUARD_ADDRESSES}
# - WIREGUARD_PUBLIC_KEY=${WIREGUARD_PUBLIC_KEY}
- WIREGUARD_PRESHARED_KEY=${WIREGUARD_PRESHARED_KEY}
# --- OpenVPN credentials (if using OpenVPN instead) ---
# - OPENVPN_USER=${OPENVPN_USER}
# - OPENVPN_PASSWORD=${OPENVPN_PASSWORD}
# --- Server selection ---
- SERVER_COUNTRIES=${SERVER_COUNTRIES}
# --- Port forwarding (ProtonVPN, AirVPN, PIA) ---
# - VPN_PORT_FORWARDING=${VPN_PORT_FORWARDING}
- FIREWALL_VPN_INPUT_PORTS=${FIREWALL_VPN_INPUT_PORTS}
- FIREWALL_OUTBOUND_SUBNETS=192.168.0.0/22
# --- General ---
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
- BLOCK_MALICIOUS=off
- HTTP_CONTROL_SERVER_ADDRESS=:8000
- HTTP_CONTROL_SERVER_LOG=on
- HTTP_CONTROL_SERVER_AUTH_DEFAULT_ROLE={"auth":"none"}
healthcheck:
test: wget -qO /dev/null http://127.0.0.1:9999 || exit 1
interval: 20s
timeout: 10s
retries: 5
restart: unless-stopped
# ============================================================
# QBITTORRENT — Torrent Client (runs through Gluetun VPN)
# ALL traffic goes through the VPN tunnel. No direct internet.
# ============================================================
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: service:gluetun
depends_on:
gluetun:
condition: service_healthy
restart: true
labels:
- deunhealth.restart.on.unhealthy=true
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
- WEBUI_PORT=8080
- TORRENTING_PORT=${FIREWALL_VPN_INPUT_PORTS}
volumes:
- qbittorrent_volume:/config
- /data:/data
healthcheck:
test: wget -q --spider http://localhost:8080 || exit 1
interval: 60s
timeout: 10s
retries: 3
start_period: 20s
restart: unless-stopped
# ============================================================
# DEUNHEALTH — Auto-restarts unhealthy containers
# If qBittorrent loses VPN connection, this restarts it automatically.
# ============================================================
deunhealth:
image: qmcgaw/deunhealth
container_name: deunhealth
network_mode: none
environment:
- LOG_LEVEL=info
- HEALTH_SERVER_ADDRESS=127.0.0.1:9999
- TZ=${TZ}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- deunhealth_volume:/config
restart: always
# ============================================================
# PROWLARR — Indexer Manager (runs through Gluetun VPN)
# Manages torrent/usenet indexers. Syncs to Radarr/Sonarr/Lidarr.
# ============================================================
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
network_mode: service:gluetun
depends_on:
gluetun:
condition: service_healthy
restart: true
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- prowlarr_volume:/config
restart: unless-stopped
# ============================================================
# FLARESOLVERR — Cloudflare Bypass (runs through Gluetun VPN)
# Some indexers use Cloudflare protection. This gets around it.
# ============================================================
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
network_mode: service:gluetun
depends_on:
gluetun:
condition: service_healthy
restart: true
volumes:
- flaresolverr_volume:/config
environment:
- LOG_LEVEL=info
- TZ=${TZ}
restart: unless-stopped
# ============================================================
# RADARR — Movie Manager (NOT behind VPN)
# Searches via Prowlarr, sends downloads to qBittorrent,
# renames and hard-links completed files to media folder.
# ============================================================
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- radarr_volume:/config
- /data:/data
ports:
- 7878:7878
networks:
arrnetwork:
ipv4_address: ${IP_RADARR}
restart: unless-stopped
# ============================================================
# SONARR — TV Show Manager (NOT behind VPN)
# Same pattern as Radarr but for TV series.
# ============================================================
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- sonarr_volume:/config
- /data:/data
ports:
- 8989:8989
networks:
arrnetwork:
ipv4_address: ${IP_SONARR}
restart: unless-stopped
# ============================================================
# LIDARR — Music Manager (NOT behind VPN)
# Optional. Comment out if you don't need music automation.
# ============================================================
lidarr:
image: lscr.io/linuxserver/lidarr:latest
container_name: lidarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- lidarr_volume:/config
- /data:/data
ports:
- 8686:8686
networks:
arrnetwork:
ipv4_address: ${IP_LIDARR}
restart: unless-stopped
# ============================================================
# BAZARR — Subtitle Manager (NOT behind VPN)
# Automatically downloads subtitles for movies and TV shows.
# ============================================================
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- bazarr_volume:/config
- /data:/data
ports:
- 6767:6767
networks:
arrnetwork:
ipv4_address: ${IP_BAZARR}
restart: unless-stopped
# ============================================================
# JELLYFIN — Media Server (NOT behind VPN)
# Your personal streaming service. Plays movies, TV, music.
# Needs full bandwidth — never put this behind the VPN.
# ============================================================
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
container_name: jellyfin
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- jellyfin_volume:/config
- /data/media:/data/media
ports:
- 8096:8096
# Uncomment the lines below to enable hardware transcoding (Intel Quick Sync / VAAPI).
# Only works if your system has Intel/AMD integrated graphics (/dev/dri must exist).
# If you get an error about /dev/dri not found, leave these commented out.
# devices:
# - /dev/dri:/dev/dri
networks:
arrnetwork:
ipv4_address: ${IP_JELLYFIN}
restart: unless-stopped
# ============================================================
# SEERR — Request System (NOT behind VPN)
# Netflix-like UI for requesting movies and TV shows.
# Share this with family — they never need to touch Radarr.
#
# Seerr is the unified successor to Overseerr and Jellyseerr
# (merged under seerr-team). Supports Plex, Jellyfin, and Emby.
#
# Config uses a NAMED Docker volume (not a bind mount). This is
# required: Seerr runs as the `node` user (UID 1000) and a
# bind-mounted host folder is created root-owned, causing a
# permission-denied crash loop. On Windows/WSL, bind mounts also
# corrupt the SQLite DB over SMB. Named volumes fix both cases
# (matches upstream Seerr docs).
#
# Migrating from ./jellyseerr or ./seerr bind mount? See README
# troubleshooting "Migrating Seerr config to a named volume".
# ============================================================
seerr:
image: ghcr.io/seerr-team/seerr:v3.0.1
init: true
container_name: seerr
environment:
- LOG_LEVEL=info
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
- PORT=5055
volumes:
- seerr_volume:/app/config # <--- Needed or will cause a permissions issue.
ports:
- 5055:5055
healthcheck:
test: wget --no-verbose --tries=1 --spider http://localhost:5055/api/v1/settings/public || exit 1
start_period: 20s
timeout: 3s
interval: 15s
retries: 3
networks:
arrnetwork:
ipv4_address: ${IP_SEERR}
restart: unless-stopped
listenarr:
image: ghcr.io/listenarrs/listenarr:canary
container_name: listenarr
ports:
- "4545:4545"
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- listenarr_volume:/app/config
- /data/media/audiobookshelf/books:/audiobooks
- /data/torrents/books:/downloads
restart: unless-stopped
networks:
arrnetwork:
ipv4_address: ${IP_LIST}
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
ports:
- 13378:80
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- /data/media/books/audiobookshelf/books:/audiobooks
- /data/media/books/audiobookshelf/podcasts:/podcasts
- /data/media/books/audiobookshelf/metadata:/metadata
- audiobookshelf_volume:/config
restart: unless-stopped
networks:
arrnetwork:
ipv4_address: ${IP_AUDIO}
navidrome:
image: deluan/navidrome:latest
container_name: navidrome
ports:
- 4533:4533
restart: unless-stopped
environment:
- ND_SCANSCHEDULE=1h
- ND_LOGLEVEL=info
- ND_SESSIONTIMEOUT=24h
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=${TZ}
volumes:
- navidrome_volume:/data
- /data/media/music:/music:ro
networks:
arrnetwork:
ipv4_address: ${IP_NAVI}
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
env_file: .env
environment:
- DOCKER_API_VERSION=1.44
- TZ=${TZ}
- PUID:=${PUID} # Reference PUID from .env
- PGID=${PGID} # Reference PGID from .env
- WATCHTOWER_CLEANUP=${WATCHTOWER_CLEANUP}
- WATCHTOWER_INCLUDE_RESTARTING=${WATCHTOWER_INCLUDE_RESTARTING}
- WATCHTOWER_ROLLING_RESTARTING=${WATCHTOWER_ROLLING_RESTARTING}
- WATCHTOWER_SCHEDULE=${WATCHTOWER_SCHEDULE}
- WATCHTOWER_INCLUDE_STOPPED=${WATCHTOWER_INCLUDE_STOPPED}
- WATCHTOWER_NOTIFICATIONS=${WATCHTOWER_NOTIFICATIONS}
- WATCHTOWER_NOTIFICATIONS_HOSTNAME=${WATCHTOWER_NOTIFICATIONS_HOSTNAME}
- WATCHTOWER_NOTIFICATION_EMAIL_FROM=${WATCHTOWER_NOTIFICATION_EMAIL_FROM}
- WATCHTOWER_NOTIFICATION_EMAIL_TO=${WATCHTOWER_NOTIFICATION_EMAIL_TO}
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER}
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT}
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER}
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=${WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD}
security_opt:
- no-new-privileges:true
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- watchtower_volume:/config
networks:
arrnetwork:
ipv4_address: ${IP_WATCH}
# Go to the VERY BOTTOM of your file (outside of services) and add this:
volumes:
gluetun_volume:
qbittorrent_volume:
deunhealth_volume:
prowlarr_volume:
flaresolverr_volume:
radarr_volume:
sonarr_volume:
lidarr_volume:
bazarr_volume:
jellyfin_volume:
seerr_volume:
listenarr_volume:
audiobookshelf_volume:
navidrome_volume:
watchtower_volume:
@@ -0,0 +1,44 @@
GNU nano 8.3 setup-folders.bat
@echo off
REM ============================================================
REM Tom Spark's ARR Stack — Folder Structure Setup (Windows)
REM https://github.com/loponai/arrstack
REM
REM Creates the folder structure required for hard links.
REM Run this ONCE before starting the stack.
REM
REM Edit DATA_DIR below if your media drive is different.
REM ============================================================
set DATA_DIR=D:\data
echo.
echo === Tom Spark's ARR Stack — Folder Setup (Windows) ===
echo.
echo Creating folder structure at %DATA_DIR%...
echo.
mkdir "%DATA_DIR%\torrents\movies" 2>nul
mkdir "%DATA_DIR%\torrents\tv" 2>nul
mkdir "%DATA_DIR%\torrents\music" 2>nul
mkdir "%DATA_DIR%\media\movies" 2>nul
mkdir "%DATA_DIR%\media\tv" 2>nul
mkdir "%DATA_DIR%\media\music" 2>nul
echo Done! Folder structure:
echo.
echo %DATA_DIR%\
echo +-- torrents\
echo ¦ +-- movies\
echo ¦ +-- tv\
echo ¦ +-- music\
echo +-- media\
echo +-- movies\
echo +-- tv\
echo +-- music\
echo.
echo IMPORTANT: For hard links to work, torrents and media
echo must be on the SAME drive (both under %DATA_DIR%).
echo.
pause
@@ -0,0 +1,72 @@
#!/bin/bash
# ============================================================
# Tom Spark's ARR Stack — Folder Structure Setup
# https://github.com/loponai/arrstack
#
# Creates the /data directory structure required for hard links
# to work correctly. Run this ONCE before starting the stack.
#
# Usage: sudo bash setup-folders.sh
# ============================================================
set -e
DATA_DIR="/data"
echo ""
echo "=== Tom Spark's ARR Stack — Folder Setup ==="
echo ""
echo "This will create the following structure:"
echo ""
echo " /data/"
echo " ├── torrents/"
echo " │ ├── movies/"
echo " │ ├── tv/"
echo " │ └── music/"
echo " └── media/"
echo " ├── movies/"
echo " ├── tv/"
echo " └── music/"
echo ""
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "ERROR: This script needs sudo to create /data and set permissions."
echo "Run: sudo bash setup-folders.sh"
exit 1
fi
# Get the real user (not root) for ownership
REAL_USER=${SUDO_USER:-$USER}
REAL_UID=$(id -u "$REAL_USER")
REAL_GID=$(id -g "$REAL_USER")
echo "Creating folders..."
mkdir -p "$DATA_DIR"/{torrents/{movies,tv,music,books},media/{movies,tv,music,books}}
echo "Setting ownership to $REAL_USER ($REAL_UID:$REAL_GID)..."
chown -R "$REAL_UID":"$REAL_GID" "$DATA_DIR"
echo "Setting permissions..."
chmod -R 775 "$DATA_DIR"
# Comment this out if you are not deploying this through Gitea CI/CD process.
echo "Setting up directory nd ownership"
# Create the directory if it doesn't exist yet
sudo mkdir -p /opt/Docker-Deployments
# Change the ownership to your deployment user
sudo chown -R miker:miker /opt/Docker-Deployments
echo ""
echo "Done! Folder structure:"
if command -v tree &> /dev/null; then
tree "$DATA_DIR"
else
find "$DATA_DIR" -type d | head -20
fi
echo ""
echo "Your PUID=$REAL_UID and PGID=$REAL_GID"
echo "Make sure these match your .env file."
echo ""
@@ -0,0 +1,329 @@
#!/bin/bash
# ============================================================
# Tom Spark's ARR Stack — Health Check & Troubleshooting
# https://github.com/loponai/arrstack
#
# Run this after 'docker compose up -d' to verify everything
# is working correctly. It checks each service, tests VPN
# connectivity, and provides specific fixes for any issues.
#
# Usage: bash test-stack.sh
# ============================================================
set -o pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
PASS="${GREEN}✓ PASS${NC}"
FAIL="${RED}✗ FAIL${NC}"
WARN="${YELLOW}! WARN${NC}"
TOTAL_PASS=0
TOTAL_FAIL=0
TOTAL_WARN=0
pass() { echo -e " ${PASS} $1"; ((TOTAL_PASS++)); }
fail() { echo -e " ${FAIL} $1"; ((TOTAL_FAIL++)); }
warn() { echo -e " ${WARN} $1"; ((TOTAL_WARN++)); }
header() { echo -e "\n${CYAN}${BOLD}[$1]${NC}"; }
fix() { echo -e " ${YELLOW}Fix: $1${NC}"; }
echo ""
echo "========================================="
echo " Tom Spark's ARR Stack — Health Check"
echo "========================================="
echo ""
# ============================================================
# TEST 1: Docker running?
# ============================================================
header "Docker"
if docker info > /dev/null 2>&1; then
pass "Docker is running"
else
fail "Docker is not running"
fix "Start Docker: sudo systemctl start docker"
fix "Or install: curl -fsSL https://get.docker.com | sh"
echo ""
echo "Cannot continue without Docker. Exiting."
exit 1
fi
# ============================================================
# TEST 2: .env file exists and has VPN credentials?
# ============================================================
header "Configuration"
if [ -f .env ]; then
pass ".env file exists"
else
fail ".env file not found"
fix "Run: cp .env.example .env && nano .env"
fix "Then fill in your VPN credentials"
fi
if [ -f .env ]; then
VPN_KEY=$(grep -E "^WIREGUARD_PRIVATE_KEY=" .env 2>/dev/null | cut -d= -f2)
VPN_PROVIDER=$(grep -E "^VPN_SERVICE_PROVIDER=" .env 2>/dev/null | cut -d= -f2)
if [ -n "$VPN_KEY" ] && [ "$VPN_KEY" != "" ]; then
pass "VPN private key is set (provider: $VPN_PROVIDER)"
else
fail "VPN private key is empty"
fix "Edit .env and paste your WireGuard private key"
fix "Get it from your VPN provider's manual setup page"
fi
fi
# ============================================================
# TEST 3: Folder structure exists?
# ============================================================
header "Folder Structure"
ALL_FOLDERS_OK=true
for dir in /data/torrents/movies /data/torrents/tv /data/torrents/music /data/torrents/books /data/media/movies /data/media/tv /data/media/music /data/media/books; do
if [ -d "$dir" ]; then
pass "$dir exists"
else
fail "$dir missing"
ALL_FOLDERS_OK=false
fi
done
if [ "$ALL_FOLDERS_OK" = false ]; then
fix "Run: sudo bash setup-folders.sh"
fi
# Check permissions
if [ -d /data ]; then
OWNER=$(stat -c '%u' /data 2>/dev/null)
ENV_PUID=$(grep -E "^PUID=" .env 2>/dev/null | cut -d= -f2)
if [ "$OWNER" = "$ENV_PUID" ] || [ "$OWNER" = "$(id -u)" ]; then
pass "/data ownership matches PUID ($OWNER)"
else
warn "/data owned by $OWNER but PUID is ${ENV_PUID:-1000}"
fix "Run: sudo chown -R ${ENV_PUID:-1000}:${ENV_PUID:-1000} /data"
fi
fi
# ============================================================
# TEST 4: Container status
# ============================================================
header "Containers"
EXPECTED_SERVICES="gluetun qbittorrent deunhealth prowlarr flaresolverr radarr sonarr lidarr bazarr jellyfin seerr"
for svc in $EXPECTED_SERVICES; do
STATUS=$(docker inspect --format '{{.State.Status}}' "$svc" 2>/dev/null)
HEALTH=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}no-healthcheck{{end}}' "$svc" 2>/dev/null)
if [ -z "$STATUS" ]; then
fail "$svc — not found (not created)"
fix "Run: docker compose up -d"
elif [ "$STATUS" = "running" ]; then
if [ "$HEALTH" = "healthy" ]; then
pass "$svc — running (healthy)"
elif [ "$HEALTH" = "unhealthy" ]; then
fail "$svc — running but UNHEALTHY"
if [ "$svc" = "gluetun" ]; then
fix "VPN probably can't connect. Check credentials in .env"
fix "Check logs: docker logs gluetun | tail -20"
fix "Try: rm -rf gluetun && docker compose up -d gluetun"
elif [ "$svc" = "qbittorrent" ]; then
fix "Usually means VPN dropped. Deunhealth should auto-restart it."
fix "Check: docker logs qbittorrent | tail -20"
fi
elif [ "$HEALTH" = "starting" ]; then
warn "$svc — running (health check starting, wait 30s and rerun)"
else
pass "$svc — running"
fi
elif [ "$STATUS" = "created" ]; then
warn "$svc — created but not started"
if [ "$svc" = "qbittorrent" ] || [ "$svc" = "prowlarr" ] || [ "$svc" = "flaresolverr" ]; then
fix "Waiting for Gluetun to be healthy. Check Gluetun status first."
fix "If Gluetun is healthy, try: docker compose up -d $svc"
elif [ "$svc" = "seerr" ]; then
fix "Port 5055 may be in use. Check: ss -tlnp | grep 5055"
fix "Or change the port in docker-compose.yml"
else
fix "Try: docker compose up -d $svc"
fi
elif [ "$STATUS" = "restarting" ]; then
fail "$svc — crash-looping (restarting)"
fix "Check logs: docker logs $svc | tail -30"
if [ "$svc" = "seerr" ]; then
fix "Seerr may have a corrupt config. Try: docker compose down seerr && rm -rf seerr && docker compose up -d seerr"
fix "WSL/Windows users: if it keeps crashing, try a named volume instead of a bind mount"
else
fix "Try: docker compose down $svc && docker compose up -d $svc"
fi
elif [ "$STATUS" = "exited" ]; then
fail "$svc — exited (crashed)"
fix "Check logs: docker logs $svc | tail -30"
fix "Try restarting: docker compose up -d $svc"
else
warn "$svc — status: $STATUS"
fi
done
# ============================================================
# TEST 5: VPN connectivity
# ============================================================
header "VPN Connection"
GLUETUN_STATUS=$(docker inspect --format '{{.State.Status}}' gluetun 2>/dev/null)
GLUETUN_HEALTH=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{end}}' gluetun 2>/dev/null)
if [ "$GLUETUN_STATUS" = "running" ] && [ "$GLUETUN_HEALTH" = "healthy" ]; then
# Get VPN IP
VPN_IP=$(docker exec gluetun wget -qO- --timeout=10 ipinfo.io/ip 2>/dev/null)
if [ -n "$VPN_IP" ]; then
pass "Gluetun VPN IP: $VPN_IP"
# Get VPN location
VPN_LOCATION=$(docker exec gluetun wget -qO- --timeout=10 "ipinfo.io/${VPN_IP}/city" 2>/dev/null)
VPN_COUNTRY=$(docker exec gluetun wget -qO- --timeout=10 "ipinfo.io/${VPN_IP}/country" 2>/dev/null)
if [ -n "$VPN_LOCATION" ]; then
pass "VPN location: $VPN_LOCATION, $VPN_COUNTRY"
fi
else
fail "Gluetun is healthy but can't reach the internet"
fix "Check logs: docker logs gluetun | tail -20"
fi
# Check if qBittorrent is tunneled
QBIT_STATUS=$(docker inspect --format '{{.State.Status}}' qbittorrent 2>/dev/null)
if [ "$QBIT_STATUS" = "running" ]; then
QBIT_IP=$(docker exec qbittorrent wget -qO- --timeout=10 ipinfo.io/ip 2>/dev/null)
if [ "$QBIT_IP" = "$VPN_IP" ]; then
pass "qBittorrent tunneled through VPN ($QBIT_IP)"
elif [ -n "$QBIT_IP" ]; then
fail "qBittorrent IP ($QBIT_IP) doesn't match VPN IP ($VPN_IP)!"
fix "This should not happen. Check network_mode in docker-compose.yml"
else
warn "Could not check qBittorrent IP (container may still be starting)"
fi
fi
# Check if Prowlarr is tunneled
PROWLARR_STATUS=$(docker inspect --format '{{.State.Status}}' prowlarr 2>/dev/null)
if [ "$PROWLARR_STATUS" = "running" ]; then
PROWLARR_IP=$(docker exec prowlarr wget -qO- --timeout=10 ipinfo.io/ip 2>/dev/null)
if [ "$PROWLARR_IP" = "$VPN_IP" ]; then
pass "Prowlarr tunneled through VPN ($PROWLARR_IP)"
elif [ -n "$PROWLARR_IP" ]; then
fail "Prowlarr IP ($PROWLARR_IP) doesn't match VPN IP ($VPN_IP)!"
fi
fi
# Verify your real IP is different
REAL_IP=$(wget -qO- --timeout=10 ipinfo.io/ip 2>/dev/null)
if [ -n "$REAL_IP" ] && [ "$REAL_IP" != "$VPN_IP" ]; then
pass "Real IP ($REAL_IP) differs from VPN IP — VPN is working!"
elif [ "$REAL_IP" = "$VPN_IP" ]; then
warn "Real IP matches VPN IP — are you already running a system-wide VPN?"
fi
else
if [ "$GLUETUN_HEALTH" = "unhealthy" ]; then
fail "Gluetun is unhealthy — VPN not connected"
fix "Check credentials in .env (these are NOT your VPN login email/password)"
fix "Check logs: docker logs gluetun 2>&1 | tail -30"
fix "Try resetting: docker compose down && rm -rf gluetun && docker compose up -d"
elif [ "$GLUETUN_HEALTH" = "starting" ]; then
warn "Gluetun health check still starting — wait 30-60 seconds and rerun"
else
warn "Gluetun not running — can't test VPN"
fix "Run: docker compose up -d"
fi
fi
# ============================================================
# TEST 6: Service web UI accessibility
# ============================================================
header "Web UI Access"
check_http() {
local name=$1 port=$2
local code=$(curl -sL -o /dev/null -w "%{http_code}" --max-time 5 "http://localhost:$port" 2>/dev/null)
if [ "$code" = "200" ] || [ "$code" = "302" ] || [ "$code" = "301" ] || [ "$code" = "307" ]; then
pass "$name — http://localhost:$port (HTTP $code)"
elif [ "$code" = "000" ]; then
# Container might be behind gluetun, check if it's running
local status=$(docker inspect --format '{{.State.Status}}' "$name" 2>/dev/null)
if [ "$status" = "running" ]; then
warn "$name — container running but port $port not reachable from host"
fix "Port may be mapped on Gluetun. Try: http://localhost:$port"
else
fail "$name — not reachable (container not running)"
fi
else
warn "$name — http://localhost:$port returned HTTP $code"
fi
}
check_http qbittorrent 8080
check_http prowlarr 9696
check_http radarr 7878
check_http sonarr 8989
check_http lidarr 8686
check_http bazarr 6767
check_http jellyfin 8096
check_http seerr 5055
# ============================================================
# TEST 7: Hard link capability
# ============================================================
header "Hard Links"
if [ -d /data/torrents ] && [ -d /data/media ]; then
# Check if same filesystem
FS_TORRENTS=$(df /data/torrents --output=source 2>/dev/null | tail -1)
FS_MEDIA=$(df /data/media --output=source 2>/dev/null | tail -1)
if [ "$FS_TORRENTS" = "$FS_MEDIA" ]; then
pass "torrents/ and media/ are on the same filesystem ($FS_TORRENTS)"
pass "Hard links will work correctly"
else
fail "torrents/ ($FS_TORRENTS) and media/ ($FS_MEDIA) are on DIFFERENT filesystems!"
fix "Hard links only work on the same filesystem/drive"
fix "Move both directories to the same drive"
fi
# Quick hard link test
TEST_FILE="/data/torrents/.hardlink_test_$$"
TEST_LINK="/data/media/.hardlink_test_$$"
if touch "$TEST_FILE" 2>/dev/null && ln "$TEST_FILE" "$TEST_LINK" 2>/dev/null; then
pass "Hard link test succeeded"
rm -f "$TEST_FILE" "$TEST_LINK" 2>/dev/null
elif [ -f "$TEST_FILE" ]; then
fail "Hard link test failed — filesystem may not support hard links"
fix "Check filesystem type: df -T /data"
fix "Hard links work on ext4, btrfs, xfs. NOT on exFAT or ntfs-3g"
rm -f "$TEST_FILE" 2>/dev/null
else
warn "Could not write to /data/torrents (permission issue?)"
fix "Run: sudo chown -R $(id -u):$(id -g) /data"
fi
else
warn "Folder structure not found — skipping hard link test"
fix "Run: sudo bash setup-folders.sh"
fi
# ============================================================
# SUMMARY
# ============================================================
echo ""
echo "========================================="
echo -e " ${GREEN}Passed: $TOTAL_PASS${NC} ${RED}Failed: $TOTAL_FAIL${NC} ${YELLOW}Warnings: $TOTAL_WARN${NC}"
echo "========================================="
if [ $TOTAL_FAIL -eq 0 ] && [ $TOTAL_WARN -eq 0 ]; then
echo -e "\n ${GREEN}${BOLD}All checks passed! Your stack is ready to go.${NC}\n"
elif [ $TOTAL_FAIL -eq 0 ]; then
echo -e "\n ${YELLOW}${BOLD}No failures, but check the warnings above.${NC}\n"
else
echo -e "\n ${RED}${BOLD}Some checks failed. Follow the fix instructions above.${NC}"
echo -e " ${BOLD}If stuck, check: docker logs <container-name>${NC}\n"
fi
@@ -0,0 +1,38 @@
### EXAMPLE DOCKER COMPOSE ###
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
# ABS runs on port 13378 by default. If you want to change
# the port, only change the external port, not the internal port
# set the container name within docker
container_name: audiobookshelf
# ports:
# - 13378:80
volumes:
# These volumes are needed to keep your library persistent
# and allow media to be accessed by the ABS server.
# The path to the left of the colon is the path on your computer,
# and the path to the right of the colon is where the data is
# available to ABS in Docker.
# You can change these media directories or add as many as you want
- ./audiobooks:/audiobooks
- ./podcasts:/podcasts
# The metadata directory can be stored anywhere on your computer
- ./metadata:/metadata
# The config directory needs to be on the same physical machine
# you are running ABS on
- ./config:/config
restart: unless-stopped
# You can use the following environment variable to run the ABS
# docker container as a specific user. You will need to change
# the UID and GID to the correct values for your user.
#environment:
# - user=1000:1000
# Network to join in docker which is named below
networks:
- proxy
# create PROXY network for NPM security. This will allow NPM to see and talk to >
networks:
proxy:
external: true
@@ -0,0 +1,24 @@
services:
emulatorjs:
image: lscr.io/linuxserver/emulatorjs:latest
container_name: emulatorjs
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /home/miker/docker/emulatorjs/config:/config
- /home/miker/docker/emulatorjs/rompack:/data
- /home/miker/docker/emulatorjs/rompack/atari2600/roms:/data/atari2600/roms:ro
- /home/miker/docker/emulatorjs/rompack/gba/roms:/data/gba/roms:ro
- /home/miker/docker/emulatorjs/rompack/n64/roms:/data/n64/roms:ro
- /home/miker/docker/emulatorjs/rompack/nes/roms:/data/nes/roms:ro
- /home/miker/docker/emulatorjs/rompack/ngp/roms:/data/ngp/roms:ro
- /home/miker/docker/emulatorjs/rompack/segaMD/roms:/data/segaMD/roms:ro
- /home/miker/docker/emulatorjs/rompack/segaMS/roms:/data/segaMS/roms:ro
- /home/miker/docker/emulatorjs/rompack/snes/rom:/data/snes/roms:ro
ports:
- 3000:3000
- 8080:80
- 4001:4001 #optional
restart: unless-stopped
@@ -0,0 +1,13 @@
services:
navidrome:
image: deluan/navidrome:latest
user: 1000:1000 # should be owner of volumes
ports:
- "4533:4533"
restart: unless-stopped
environment:
# Optional: put your config options customization here. Examples:
# ND_LOGLEVEL: debug
volumes:
- /home/miker/docker/navidrome/data:/data
- /home/miker/docker/data/music:/music:ro
@@ -0,0 +1,21 @@
services:
metube:
image: alexta69/metube:latest
container_name: metube
ports:
- "7000:8080"
volumes:
- /home/miker/docker/metube/downloads:/downloads # Mounts a local 'downloads' directory to the container's /downloads
- /home/miker/docker/metube/config:/config # Mounts a local 'config' directory for MeTube's configuration
- /home/miker/docker/metube/.cache:/.cache
environment:
- DOWNLOAD_DIR=/downloads
- UID=1000
- GID=1000
- DEFAULT_THEME=dark
- URL_PREFIX=/metube # If running behind a reverse proxy with a subpath
- 'YTDL_OPTIONS={"format": "best", "exec": "ffmpeg -i %(filepath)q -c:v libx264 -c:a aac %(filepath)q.h264.mp4"}'
- HTTPS=true # If you're directly configuring HTTPS in MeTube
- OUTPUT_TEMPLATE=%(title)s.%(id)s.%(ext)s # Corrected format
- DELETE_FILE_ON_TRASHCAN=true
restart: unless-stopped
@@ -0,0 +1,91 @@
build the server
1. ) sudo -i # Switch to root
2.) apt update && apt upgrade -y
3.) Create /data directory structure.
sudo mkdir -p /data/{torrents/{movies,tv,music},media/{movies,tv,music}}
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data
4.) Install fail2ban then stop and disable it (as it freezes the install program)
Stop service: sudo systemctl stop fail2ban
Disable on boot: sudo systemctl disable fail2ban
4.) curl -fsSL https://get.tomsparkbox.com/install.sh | sudo bash
5.) Run through the checklist wizard.
6.) When the wizard is done, you will need to go into the gluetun config file and add the preshared key and the FIREWALL_VPN_INPUT_PORTS
# Edit your credentials
nano /opt/sparkbox/.env
# Fix VPN_PROVIDER, WIREGUARD_PRIVATE_KEY, etc.
sparkbox restart media
7.) Go into the systems and set them up.
Prawlarr up a couple of indexers.
radaar, sonarr, and qbittorrent and setup the login page
qbit login and change password (password in /opt/sparkbox/state/qbittorrent-admin-password.txt)
Jellyfin wizard + Seerr sign-in, run:
9.) (Auto) Libraries created by media-finish, run sudo sparkbox media-finish
Finding Auto-Generated Credentials
SparkBox pre-seeds a few service passwords and stores them in plaintext files under /opt/sparkbox/state/:
Service File Where to enter it
Portainer portainer-admin-password.txt First login at :9000
qBittorrent qbittorrent-admin-password.txt First login at :8089 (user admin)
Sonarr / Radarr / Prowlarr auto-configured by arr-bootstrap Auth disabled on LAN; no password needed
Jellyfin none — you pick it First-run wizard at :8096
Nginx Proxy Manager none — upstream default First login: admin@example.com / changeme
"Root Folder Does Not Exist" in Radarr/Sonarr
The /data directory structure has not been created.
sudo mkdir -p /data/{torrents/{movies,tv,music},media/{movies,tv,music}}
sudo chown -R $(id -u):$(id -g) /data
sudo chmod -R 775 /data
/data/
├── torrents/ # qBittorrent downloads here
│ ├── movies/
│ ├── tv/
│ └── music/
└── media/ # Radarr/Sonarr organize here (Jellyfin reads from here)
├── movies/
├── tv/
└── music/
CLI Quick Reference
bash
Copy
sparkbox status # See what's running
sparkbox up # Start all enabled modules
sparkbox down # Stop everything
sparkbox restart # Restart all services
sparkbox restart privacy # Restart just one module
sparkbox update # Pull latest app images
sparkbox upgrade # Upgrade SparkBox itself to the latest release
sparkbox logs sb-NAME # View service logs
sparkbox modules # List all modules
sparkbox enable cloud # Enable a module
sparkbox disable files # Disable a module
sparkbox backup # Create a backup
sparkbox restore FILE # Restore from backup
sparkbox urls # Show all service URLs
sparkbox help # Full command list
Uninstalling
bash
Copy
sparkbox down
rm -rf /opt/sparkbox
rm -f /usr/local/bin/sparkbox
@@ -0,0 +1,27 @@
services:
beszel:
image: henrygd/beszel:latest
container_name: beszel
restart: unless-stopped
ports:
- 8090:8090
volumes:
- /home/miker/docker/beszel/data:/beszel_data
- /home/miker/docker/beszel/socket:/beszel_socket
# Agent Install
# beszel-agent:
# image: henrygd/beszel-agent:latest
# container_name: beszel-agent
# restart: unless-stopped
# network_mode: host
# volumes:
# - /home/miker/docker/beszel/agent:/var/lib/beszel-agent
# - /home/miker/docker/beszel/socket:/beszel_socket
# - /var/run/docker.sock:/var/run/docker.sock:ro
# environment:
# LISTEN: /beszel_socket/beszel.sock
# HUB_URL: http://192.168.2.7:8090
# TOKEN: <token>
# KEY: "<key>"
@@ -0,0 +1,35 @@
services:
checkmk:
container_name: checkmk
image: checkmk/check-mk-raw:2.3.0p7
tmpfs:
- /opt/omd/sites/cmk/tmp:uid=1000,gid=1000
ulimits:
nofile: 1024
volumes:
- /home/miker/docker/checkmk/monitoring:/omd/sites
- /etc/localtime:/etc/localtime:ro
environment:
- /var/run/docker.sock:/var/run/docker.sock
- TZ=America/New_York
ports:
- "8080:8000"
- "5000:5000"
restart: unless-stopped
networks:
checkmk:
networks:
checkmk:
# When build is complete, you need to look in the logs for the default username and password.
# Passwd for checkmk - Uv8bJ0nDFxLxXz9qJxe0
# To install the agent on a linux system:
# sudo wget http://192.168.0.183:8180/cmk/check_mk/agents/check-mk-agent_2.3.0p7-1_all.deb
# sudo dpkg -i check-mk-agent_2.3.0p7-1_all.deb
scp check-mk-agent_2.3.0p7-1_all.deb miker@107.175.245.171 /home/miker
@@ -0,0 +1,18 @@
services:
gotify:
image: gotify/server
container_name: gotify
volumes:
- /home/miker/dockerapps/gotify:/app/data
restart: unless-stopped
# ports:
# - '8091:80'
security_opt:
- no-new-privileges:true
networks:
proxy:
environment:
- TZ=America/New_York
networks:
proxy:
external: true
+14
View File
@@ -0,0 +1,14 @@
services:
uptime-kuma:
container_name: uptime-kuma
image: elestio/uptime-kuma
environment:
- DB_TYPE=sqlite
- DB_STORAGE=/app/data/uptime-kuma.db
- SOFTWARE_VERSION_TAG=latest
- URL=https://kuma.mikeandrebel.com
volumes:
- /home/miker/docker/kuma/data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
restart: always
@@ -0,0 +1,73 @@
services:
postgresql-server:
image: postgres:latest
container_name: postgresql-server
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgresql-data:/var/lib/postgresql/data
zabbix-server:
image: zabbix/zabbix-server-pgsql:latest
container_name: zabbix-server
restart: unless-stopped
depends_on:
- postgresql-server
environment:
DB_SERVER_HOST: postgresql-server
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "10051:10051"
volumes:
- zabbix-server-data:/var/lib/zabbix
- zabbix-snmptraps-data:/var/lib/zabbix/snmptraps
- zabbix-export-data:/var/lib/zabbix/export
zabbix-web-nginx-pgsql:
image: zabbix/zabbix-web-nginx-pgsql:latest
container_name: zabbix-web
restart: unless-stopped
depends_on:
- postgresql-server
- zabbix-server
environment:
DB_SERVER_HOST: postgresql-server
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ZBX_SERVER_HOST: zabbix-server
PHP_TZ: ${PHP_TZ}
ports:
- "${ZABBIX_FRONTEND_PORT}:8080"
volumes:
- zabbix-web-data:/usr/share/zabbix
zabbix-agent:
image: zabbix/zabbix-agent:latest
container_name: zabbix-agent
restart: unless-stopped
depends_on:
- zabbix-server
environment:
ZBX_HOSTNAME: "zabbix-server"
ZBX_SERVER_HOST: zabbix-server
ZBX_SERVER_PORT: '10051'
ZBX_SERVER_ACTIVE: zabbix-server
volumes:
- /:/rootfs:ro
- /var/run:/var/run
privileged: true
volumes:
postgresql-data:
zabbix-server-data:
zabbix-snmptraps-data:
zabbix-export-data:
zabbix-web-data:
@@ -0,0 +1,5 @@
POSTGRES_USER=zabbix
POSTGRES_PASSWORD=EpvfxaNSbtaJ6HYBQCZl
POSTGRES_DB=zabbix
PHP_TZ=America/New_York
ZABBIX_FRONTEND_PORT=4080
+12
View File
@@ -0,0 +1,12 @@
services:
dozzle-agent:
image: amir20/dozzle:latest
container_name: dozzle-agent
command: agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 7007:7007
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock # Use Docker Socket Proxy instead for improved security
# DOCKER-LABELS-PLACEHOLDER
+22
View File
@@ -0,0 +1,22 @@
services:
dozzle:
container_name: dozzle
image: amir20/dozzle:latest
security_opt:
- no-new-privileges:true
environment:
- DOZZLE_REMOTE_AGENT=192.168.2.16:7007
- DOZZLE_LEVEL=info
- DOZZLE_TAILSIZE=300
- DOZZLE_FILTER="status=running"
# - DOZZLE_FILTER="label=log_me" # limits logs displayed to containers with this label
- DOCKER_HOST
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 8089:8080
restart: unless-stopped
@@ -0,0 +1,26 @@
services:
speedtest-tracker:
image: lscr.io/linuxserver/speedtest-tracker:latest
container_name: speedtest-tracker
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["apps", "all"]
networks:
- default
ports:
- "$SPEEDTESTTRACKER_PORT:80"
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
DB_CONNECTION: sqlite
APP_KEY: $SPEEDTEST_TRACKER_APP_KEY #optional
# DB_HOST: mariadb #optional
# DB_PORT: 3306 #optional
# DB_DATABASE: speedtest #optional
# DB_USERNAME: speedtest_user #optional
# DB_PASSWORD: ST-DB-PASSWORD-PLACEHOLDER #optional
volumes:
- $DOCKERDIR/appdata/speedtest-tracker:/config
# DOCKER-LABELS-PLACEHOLDER
+62
View File
@@ -0,0 +1,62 @@
version: "3.9"
# Common defaults for all services - use with: <<: *default-service
x-common: &default-service
restart: unless-stopped
env_file:
- .env
networks:
- home_network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
services:
my-service:
# Inherit common defaults
<<: *default-service
# Use specific image version tag, never :latest
image: example/app:1.2.3
# Environment Management
environment:
TZ: America/New_York
PUID: "1000"
PGID: "1000"
# Add service-specific env vars here
# Networking - port mapping (optional)
ports:
- "8080:8080"
# Resource Constraints (native Compose limits, not Swarm deploy)
mem_limit: 512M
cpus: 0.5
# Persistent Storage
volumes:
- ./config:/config
- ./data:/data
- /etc/localtime:/etc/localtime:ro
# Health Checks (customize based on your service)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
home_network:
external: true
# If proxy_network does not exist, create it first:
# docker network create proxy_network
# Alternatively, manage the network in this file:
# driver: bridge
# driver_opts:
# com.docker.network.bridge.name: "br-proxy"
+5
View File
@@ -0,0 +1,5 @@
WEBPASSWORD=e6z1EBVDERIP7
PUID=1000
PGID=1000
# TZ for container timezone
TZ=America/New_York
@@ -0,0 +1,40 @@
---
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
env_file:
- .env
ports:
- "53:53/tcp"
- "53:53/udp"
- "67:67/udp"
- "500:80/tcp"
# - "443:443/tcp"
networks:
pihole_internal:
ipv4_address: 172.70.9.3
pihole:
environment:
TZ: ${TZ}
PUID: ${PUID} # Reference PUID from .env
PGID: ${PGID} #
WEBPASSWORD: ${WEBPASSWORD}
PIHOLE_DNS_: '172.70.9.2#5053'
DNSMASQ_LISTENING: 'all'
VIRTUAL_HOST: pihole.mikemcfetridge.com
volumes:
- /home/miker/dockerapps/pihole/appdata/pihole/etc:/etc/pihole
- /home/miker/dockerapps/pihole/appdata/pihole/etc/dnsmasq.d:/etc/dnsmasq.d
- /home/miker/dockerapps/pihole/appdata/pihole/log:/var/log/pihole
restart: unless-stopped
networks:
pihole_internal:
ipam:
config:
- subnet: 172.70.9.0/29
name: pihole_internal
pihole:
external: true
+41
View File
@@ -0,0 +1,41 @@
services:
# Pi-hole - DNS Server and AdBlocker
pihole:
image: pihole/pihole:latest
container_name: pihole
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["all", "core"]
networks:
- default
# cap_add:
# # See https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
# # Required if you are using Pi-hole as your DHCP server, else not needed
# - NET_ADMIN
# # Required if you are using Pi-hole as your NTP client to be able to set the host's system time
# - SYS_TIME
# # Optional, if Pi-hole should get some more processing time
# - SYS_NICE
ports:
- "53:53/tcp"
- "53:53/udp"
# - "67:67/udp" # DHCP Server
- "80:80/tcp" # HTTP
# - "123:123/udp" # NTP
# - "443:443" # HTTPS
volumes:
- "/home/miker/dockerapps/pihole/appdata/pihole/etc:/etc/pihole"
- "/home/miker/dockerapps/pihole/appdata/pihole/etc/dnsmasq.d:/etc/dnsmasq.d"
- "/home/miker/dockerapps/pihole/appdata/pihole/log:/var/log/pihole"
environment:
# https://docs.pi-hole.net/docker/upgrading/v5-v6/?h=pihole_dns#misc Variables list for v6
TZ: ${TZ}
FTLCONF_webserver_api_password: !Sucyetat123
FTLCONF_dns_listeningMode: 'all'
FTLCONF_dns_upstreams: 9.9.9.9;1.1.1.1;192.168.2.5
FTLCONF_dns_bogusPriv: "true"
FTLCONF_dns_domainNeeded: "true"
FTLCONF_dns_domain: home.mikemcfetridge
FTLCONF_webserver_interface_theme: "default_dark"
# DOCKER-LABELS-PLACEHOLDER
@@ -0,0 +1,7 @@
#run these commands to fix port bind error
sudo sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf
sudo sh -c 'rm /etc/resolv.conf && ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf'
systemctl restart systemd-resolved
@@ -0,0 +1,51 @@
services:
dns-server:
container_name: technitium-dns-server
hostname: dns-server
image: technitium/dns-server:latest
# For DHCP deployments, use "host" network mode and remove all the port mappings, including the ports array by commenting them
# network_mode: "host"
ports:
- "5380:5380/tcp" #DNS web console (HTTP)
- "5381:53443/tcp" #DNS web console (HTTPS)
- "53:53/udp" #DNS service
- "53:53/tcp" #DNS service
# - "853:853/udp" #DNS-over-QUIC service
# - "853:853/tcp" #DNS-over-TLS service
# - "443:443/udp" #DNS-over-HTTPS service (HTTP/3)
# - "443:443/tcp" #DNS-over-HTTPS service (HTTP/1.1, HTTP/2)
# - "80:80/tcp" #DNS-over-HTTP service (use with reverse proxy or certbot certificate renewal)
- "8053:8053/tcp" #DNS-over-HTTP service (use with reverse proxy)
# - "67:67/udp" #DHCP service
environment:
- DNS_SERVER_DOMAIN=technitium-dns-server #The primary domain name used by this DNS Server to identify itself.
- DNS_SERVER_ADMIN_PASSWORD=password #DNS web console admin user password.
# - DNS_SERVER_ADMIN_PASSWORD_FILE=password.txt #The path to a file that contains a plain text password for the DNS web console admin user.
# - DNS_SERVER_PREFER_IPV6=false #DNS Server will use IPv6 for querying whenever possible with this option enabled.
# - DNS_SERVER_WEB_SERVICE_LOCAL_ADDRESSES=172.17.0.1,127.0.0.1 #Comma separated list of network interface IP addresses that you want the web service to listen on for requests. The "172.17.0.1" address is the built-in Docker bridge. The "[::]" is the default value if not specified. Note! This must be used only with "host" network mode.
- DNS_SERVER_WEB_SERVICE_HTTP_PORT=5380 #The TCP port number for the DNS web console over HTTP protocol.
# - DNS_SERVER_WEB_SERVICE_HTTPS_PORT=53443 #The TCP port number for the DNS web console over HTTPS protocol.
# - DNS_SERVER_WEB_SERVICE_ENABLE_HTTPS=false #Enables HTTPS for the DNS web console.
# - DNS_SERVER_WEB_SERVICE_USE_SELF_SIGNED_CERT=false #Enables self signed TLS certificate for the DNS web console.
- DNS_SERVER_OPTIONAL_PROTOCOL_DNS_OVER_HTTP=false #Enables DNS server optional protocol DNS-over-HTTP on TCP port 8053 to be used with a TLS terminating reverse proxy like nginx.
# - DNS_SERVER_RECURSION=AllowOnlyForPrivateNetworks #Recursion options: Allow, Deny, AllowOnlyForPrivateNetworks, UseSpecifiedNetworks.
# - DNS_SERVER_RECURSION_DENIED_NETWORKS=1.1.1.0/24 #Comma separated list of IP addresses or network addresses to deny recursion. Valid only for `UseSpecifiedNetworks` recursion option.
# - DNS_SERVER_RECURSION_ALLOWED_NETWORKS=127.0.0.1, 192.168.1.0/24 #Comma separated list of IP addresses or network addresses to allow recursion. Valid only for `UseSpecifiedNetworks` recursion option.
# - DNS_SERVER_ENABLE_BLOCKING=false #Sets the DNS server to block domain names using Blocked Zone and Block List Zone.
# - DNS_SERVER_ALLOW_TXT_BLOCKING_REPORT=false #Specifies if the DNS Server should respond with TXT records containing a blocked domain report for TXT type requests.
# - DNS_SERVER_BLOCK_LIST_URLS= #A comma separated list of block list URLs.
- DNS_SERVER_FORWARDERS=1.1.1.1, 8.8.8.8 #Comma separated list of forwarder addresses.
- DNS_SERVER_FORWARDER_PROTOCOL=Tcp #Forwarder protocol options: Udp, Tcp, Tls, Https, HttpsJson.
# - DNS_SERVER_LOG_USING_LOCAL_TIME=true #Enable this option to use local time instead of UTC for logging.
volumes:
- ./config:/etc/dns
restart: unless-stopped
sysctls:
- net.ipv4.ip_local_port_range=1024 65000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5380"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
+4
View File
@@ -0,0 +1,4 @@
WG_HOST=wireguard.mikemcfetridge.com # Public IP or domain name whe>
PASSWORD="!Sucyetat123" # Password for accessing the WG-Easy web i>
PORT=51821 # Port for the web interface.
WG_PORT=51820
@@ -0,0 +1 @@
MP2igeozMSf2rqWLbvgHXDgoI9tOpe5nzscufB89xno=
@@ -0,0 +1 @@
3jCGyeho+tdqj6yrLa6NY5HAkQkVP+kL/ekHMgEyWHk=
@@ -0,0 +1,12 @@
[Interface]
PrivateKey = 4IliWQXfjdWFGafNmhCUnAaFdEQhdspjv0awh2MJ2XE=
Address = 10.8.0.4/24
DNS = 1.1.1.1
[Peer]
PublicKey = yqo3AIh4UpZMJm439z/ic++/op3T0Jrt27wBfgDthhY=
PresharedKey = dzf33VaZ0mSbS5HEVoLS3JqWsPP1tiwGTedEDLnqNuY=
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 0
Endpoint = wgeasy.mikemcfetridge.com:51820
@@ -0,0 +1,30 @@
Wireguard Made Easy
# Need to setup portforwarding on your router for 51820
# Enter domain url wg.example.com
# Enter in a password, this is for the gui.
# Docs - https://docs.techdox.nz/wgeasy/
version: '3.8'
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy # The Docker image to use.
container_name: wireguard # Name of the container.
environment: # Environment variables to configure the instance.
- LANG=en # Language settings.
- WG_HOST=wg.example.com # Public IP or domain name where WG-Easy is accessible.
- PASSWORD=<Password> # Password for accessing the WG-Easy web interface.
- PORT=51821 # Port for the web interface.
- WG_PORT=51820 # WireGuard port for VPN traffic.
volumes:
- /home/docker/wireguard:/etc/wireguard # Volume mapping for WireGuard configuration files.
ports:
- "51820:51820/udp" # UDP port used by WireGuard.
- "51821:51821/tcp" # TCP port for accessing the web interface.
cap_add: # Capabilities required for managing networking features.
- NET_ADMIN
- SYS_MODULE
sysctls: # Kernel parameters that need to be set for WireGuard.
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped # Ensures the container restarts automatically unless manually stopped.
@@ -0,0 +1,34 @@
---
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest # The Docker image to use.
container_name: wireguard # Name of the container.
env_file:
- .env
environment: # Environment variables to configure the ins>
- LANG=en # Language settings.
- WG_HOST=${WG_HOST} # Public IP or domain name whe>
- PASSWORD=${PASSWORD} # Password for accessing the WG-Easy web i>
- PORT=${PORT} # Port for the web interface.
- WG_PORT=${WG_PORT} # WireGuard port for VPN traffic.
volumes:
- ./data:/etc/wireguard # Volume mapping for WireG>
ports:
- "51820:51820/udp" # UDP port used by WireGuard.
- "51821:51821/tcp" # TCP port for accessing the web interface.
cap_add: # Capabilities required for managing network>
- NET_ADMIN
- SYS_MODULE
sysctls: # Kernel parameters that need to be set for >
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped # Ensures the container restarts automatical>
# Network to join in docker which is named below
networks:
- proxy
# create PROXY network for NPM security. This will allow NPM to see and talk to >
networks:
proxy:
external: true
@@ -0,0 +1,249 @@
# Setting up a Remote Access Wireguard VPN with Netmaker
<iframe allowfullscreen="allowfullscreen" height="314" src="https://www.youtube.com/embed/X-BYDYoM_3w" width="560"></iframe>
In Business, or home use, networking is often a very *tough nut to crack*. In today's world, where we choose privacy over convenience, the search continues for solutions to help us have a seamless experience in accessing our data, applications, system, and media. I've done literally hundreds of videos on so many amazing applications, but at the end of the day it all comes down to access. We want to have access to our systems regardless of where we are in the world.
Netmaker is an absolute beast of a tool that provides exactly what we are all looking for. It runs on top of the very secure, very fast, Wireguard protocol. This means we can setup a cloud based network, but make it feel like we are right there at our office, or in our home on the local network. The little bit of what I'll discuss in this post, and demonstrate to you in the video is just the tip of the metaphorical iceberg. The limitations are only what you can think up for your networking needs. But tools like Netmaker are what make open source such a valuable asset.
### Installation
#### What we'll cover today
1. How to install the Netmaker server, and create a network.
2. How to install the Netmaker Client and join a network.
3. How to setup Ingress and Egress nodes (and what those are).
4. How to setup external clients (and what that means)
5. How to connect back to our Local Area Network without opening any firewall ports.
#### What you'll need
- A server with a static public IP address. (I used a Digital Ocean droplet with 1 vCPU and 1 GB RAM)
- A machine inside our Local Area network that we can use as a gateway device on our Wireguard network.
- A machine we want to connect to our LAN from outside (a smart phone, tablet, laptop, or even a machine on another network somewhere).
- Preferably a Domain name that you own, and can add a subdomain and A-record DNS entry for.
- Docker and Docker-Compose on our server.
- About 30 minutes of time.
##### Installing the Server
As always, I highly recommend that you follow the instructions on the netmaker site for server installation, as this will be the most up to date method based on version. As of the making of this article and accompanying video, Netmaker is on version 0.16.0. You can find [their instructions for installation here.](https://docs.netmaker.org/quick-start.html)
First, we'll need a server (netmaker is known to run well on Ubuntu servers, so I made mine 22.04) to install the Netmaker server on. I highly recommend setting this up on a ninexpensive VPS. If you use [my Digital Ocean link](https://m.do.co/c/a6a61ae5524) you can sign up for a 60 Day / $200.00 US trial. Which means you'll pay nothing for the first 2 months of the $6 server we'll use for this purpose. That said, feel free to use a server on Linode, Vultr, Hertzner, or whoever you prefer.
<p class="callout warning">NOTE: The Netmaker folks have identified issues with using the Oracle Cloud Free Tier server for Wireguard in general, and do not suggest you use that service for this purpose.</p>
When you've got your server created, you'll get a public IPv4 IP address assigned to it. You'll want to get that IPv4 address, and create an A-record for a subdomain on a domain that you own. I own the domain "theitpros.org", so I setup an A-record in my registrar's DNS settings to point the subdomain of "\**.*netmaker.theitpros.org" to my public IPv4 address. You should do something similar, with your domain. Create an A-record for "*\**.netmaker.&lt;your-super-domain&gt;" pointing to your server's IPv4 address.
Next, you'll need to install Docker and Docker-Compose. In the Netmaker instructions they give you a single command to install docker.io, docker-compose, and Wireguard on a server. I however, prefer to use Docker-CE to docker.io. It's completely up to you, and the system works with either one.
Follow these steps to install Docker-CE and Docker-Compose using my method:
```
wget https://gitlab.com/bmcgonag/docker_installs/-/raw/main/install_docker_nproxyman.sh
```
Next, change the permissions on the downloaded file:
`chmod +x install_docker_nproxyman.sh`
And run the script with:
`./install_docker_nproxyman.sh`
Now, select your base OS by number. Next, you need to answer "y" to the first two questions about installing Docker-CE and Docker-Compose. Feel free to answer "n" to the other questions. The script will attempt to update and upgrade your install first.
Next, install Wireguard with one of the following commands:
Ubuntu:
`sudo apt install wireguard -y`
Fedora
`sudo dnf install wireguard-tools -y`
Arch
`sudo pacman -S wireguard-tools`
<p class="callout info">Please run any upgrades before running the script, as many VPS upgrades will show a prompt that my script down not display.</p>
After the script completes, you can continue with the installation of Netmaker.
If you have a firewall enabled in front of your chosen server, you'll need to open a few ports to allow Wireguard traffic to flow (this is why we want this installed in a VPS).
Open Ports:
- 443 (TCP)
- 51821-51899 (UDP) You can make this range smaller, but if you want to eventually setup more than one network, you'll want to have a port for each network to run on.
```
sudo ufw allow proto tcp from any to any port 443 && sudo ufw allow 51821:51830/udp
```
```
iptables --policy FORWARD ACCEPT
```
Next, you'll get your docker-compose.yml file ready. First run the command:
```
ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'
```
The following lines will pull down the docker-compose.yml file that's already prepared, from the Netmaker repository. After that you'll replace the parts surrounded with angle-brackets "&lt;" and "&gt;" with your actual base subdomain (mine was "netmaker.theitpros.org"). You'll need to replace the angle-brackets themselves as well. For IP, put the public IPv4 address you received for your server. For email, you can use any email you want.
```
wget -O docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.yml
sed -i 's/NETMAKER_BASE_DOMAIN/<your base domain>/g' docker-compose.yml
sed -i 's/SERVER_PUBLIC_IP/<your server ip>/g' docker-compose.yml
sed -i 's/YOUR_EMAIL/<your email>/g' docker-compose.yml
```
Next, we'll create a secret key value for our server as well.
```
tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo ''
```
And now we'll place that key value into the docker-compose file using the same method as the commands above. Replace the value with the angle brackets, **including the angle-brackets themselves**, with the actual key value.
```
sed -i 's/REPLACE_MASTER_KEY/<your generated key>/g' docker-compose.yml
```
Now we need to download one more file. The last file we need is a Mosquitto (MQTT Server) file. Mosquitto is a message queue server application. This is the software that allows messages to be sent and recieved on a network, and is part of how netmaker keeps your client machines in sync.
```
wget -O /root/mosquitto.conf https://raw.githubusercontent.com/gravitl/netmaker/master/docker/mosquitto.conf
```
<p class="callout info">NOTE: After version 0.16.1, you'll need to do two more steps.
1. Cretae a unique, strong password for the Mosquitto configuration file, and add it to the docker-compose.
2. Download a "wait" script, for use on the server.
The commands for these 2 steps are:
</p>
```
sed -i "s/REPLACE_MQ_ADMIN_PASSWORD/<mqtt admin password>/g" docker-compose.yml
```
<p class="callout info">Where you'll replace the the angle-brackets and information between them with a password of your creation.</p>
```
wget -q -O /root/wait.sh https://raw.githubusercontent.com/gravitl/netmaker/develop/docker/wait.sh
chmod +x wait.sh
```
We are now ready to start the Netmaker server with the following command:
`sudo docker-compose up -d`
Once the images have all been pulled down, and you have started your containers (you see 'done' in the command line for all containers, and you are back at the prompt), you'll want to open your favorite modern browser of choice, and navigate to the address:
https://dashboard.netmaker.&lt;your super domain&gt;
In my case, I went to
[https://dashboard.netmaker.theitpros.org](https://dashboard.netmaker.theitpros.org)
Once at your new domain, you'll be prompted to create an account. Enter an email you want, doesn't have to be the same as the one you put in the docker-compose file, and a strong password.
Once saved, you'll be taken to the login screen. Lovin, and you'll see your Netmaker dashboard.
##### Create a Network
Now, you'll want to create a network in Netmaker. Click the 'Networks' tile on the dashboard, or in the left navigation panel click 'Networks'.
On the Networks screen, you'll want to click on the 'Create Network' button.
In the subsequent form, give you network a name, and then enter a private IP address and range in CIDR notation, you'd like for the network. Alternatively, there is an 'Autofill' button you can click, and then change the name, or network IP generated by the autofill option.
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/image.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/image.png)
Next, select whether to add IPv6, and whether you want UDP hole punching to be enabled. UDP hole punching can be usefule, but is not always necessary. If you leave it off, and later determine you want to enable it, you can edit the network.
Finally, after selecting the options you want, click 'Create Network'.
You new network will be created if you have entered a name that is 12 characters or less, and have not tried to enter an improper IP address and / or range.
#### Netmaker Client Installation
Now that you have a Wireguard server in the cloud, running Netmaker, you'll likely want to connect a machine or 50 to the network. The easiest way with the most benefits is to use the Netmaker client. This is a tool you install on any machine / device that you want to be able to connect to the Netmaker network / server you just created. There are clients for MacOS, Linux, BSD, and yes...even Windows. There is also a graphical client (GUI) which is installed by default on Windows and MacOS, but is a separate install for Linux / BSD. You can download the GUI for Linux and BSD on their [Github releases page](https://github.com/gravitl/netmaker/releases).
To avoid causing issues, I will only provide the link to the client install page. There are instructions for many different distributions there, and as those change, I don't want my documentation to give bad information. [Please go to the official Netmaker documentation pages for the proper installation instruction of the Netclient](https://docs.netmaker.org/netclient.html).
Once you have the Netclient installed, you'll want to go back to your Web Admin page, and go to "Access Keys" in the dashboard or left navigation.
Once in Access Keys, select the network you'd like to add a node for.
<p class="callout info">You can add multiple networks in Netmaker, and add nodes to each network individually. A single machine can be a node on multiple networks by using the Netclient to join those networks.</p>
Then click Create Access Key.
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/n4Yimage.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/n4Yimage.png)
Next, increase the number of access keys. You can use an access key more than once. You could provide Fred an access key for his Laptop, Desktop, and CI Server. You only need to create once access key for Fred, but allocate it for three (3) uses.
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/ySjimage.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/ySjimage.png)
Now click the 'Create' button.
You'll see a bunch of different lines with options for adding the key to various nodes.
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/TrJimage.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/TrJimage.png)
<p class="callout warning">Don't worry, the example join above isn't functional anymore.</p>
Now, you'll paste this into the terminal of your desired client, and be joined to the network. You should now be able to ping the IP of your server machine, and any other peer machines on the network. If you have trouble joining with the Netclient, you can start through any troubleshooting steps in the Netmaker documentation, and you can always jump over to their Discord server to ask for help as well.
#### Ingress and Egress Nodes
The terminology is set from the perspective of the internet to the Netmaker netwrok. Ingress, meaning to come into, or a path inward is therefore indicative of a node that has been setup and identified as a method for an external client to connect the Network (we'll cover connecting external clients in the next section). Egress, means a point where the client machine can exit the Netmaker network, and go into another network such as a LAN inside your home or business.
What we want to setup is a network where we have an external client or netclient node external to our LAN, that can then successfully get back into our LAN via our Netmaker network, and access our various servers / machines / services inside the LAN.
In order to set this up, we'll need to first identify an Egress Node. Inside our Local Area Network, we'll install the Netclient, and join our Netmaker Network with it. You'll be able to see the node show up in the Netmaker Web Admin panel on the 'Nodes' view.
Next to this node, you'll see several icons. One is in the column form Egress. Click this corresponding icon,
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/Vxtimage.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/Vxtimage.png)
You'll be prompted for two pieces of information.
1\. The network interface you'll be using as the Egress point. You can find your network information from the command line with the command:
`ip -br -c a`
2\. The IP address range in CIDR notation, you want give access to on this network. In my case, my network is 192.168.10.x, so to give an external device access to the whole network, I would make the entry be
`192.168.10.0/24`
Now click on Create Egress. You should see the double arrow icon turn into a check mark for that node.
With your Egress gateway defined, you should now be able to ping any machine that can respond to ping requests that resides inside you selected LAN, from outside that LAN.
Next, we'll create an Ingress node. You generally don't want this to be the same node as your egress node, but it can be done. I simply setup my main Netmaker server node as my Ingress node. An ingress node is used to allow an "external" client into the netmaker network. Some machines / devices won't be running the Netclient, but fear not! These devices can still run Wireguard, and connect to the network using the External Client configuration file that can be generated in the server. Then simply use the wireguard app on mobile devices, or the CLI or a graphical Wireguard tool to connect these devices to your Netmaker networks.
Before you can create any external client configurations, you need to define an "Ingress" node. So, let's just use our Netmaker server for this...but note that any host that is reachable by a device can be used, but a public, static IP is preferred for this purpose.
In your 'Nodes' view, find your Netmaker server, and click the icon in its row that is in the "Ingress" column. Accept the prompt, and you should see the double tail icon turn into a check mark. You're done. You can now start creating External Clients.
#### External Clients
External clients are clients that will not have their setting synced when changes to the networks / other nodes are made. These are clients setup with a static configuration file. These clients will be able to connect to your network, and interact just fine, but don't get any automatic updates from the network. So, when possible, the Netclient (IMO) is a better choice.
Click on the 'External Clients' option from the dashboard, or from the left navigation panel. Select the Network for which you wish to create an external client. On the left side of the interface, you'll see a list of any Ingress nodes you've defined. Click the "+" icon next to the node you want to create external clients for.
[![image.png](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/scaled-1680-/g0Jimage.png)](https://wiki.opensourceisawesome.com/uploads/images/gallery/2022-10/g0Jimage.png)
On the right side you'll see External Client configurations being created. Feel free to click the QR Code to expand it onto the screen, or to download the client configuration to your desktop / laptop and use that to connect.
Notice, you can delete a client at any time, and you can remove / disable netclients, and change nodes back from being Egress or Ingress points. You really have total control over your Netmaker network from the ADmin dashboard and tools.
You now have the tools to make all kinds of amazing networks from Netmaker using Wireguard. Go forth, and be awesome!
#### Support my Channel and ongoing efforts through Patreon:
[https://www.patreon.com/bePatron?u=234177](https://www.patreon.com/bePatron?u=234177)
@@ -0,0 +1,35 @@
services:
# WG-EASY - WireGuard Easy
wg-easy:
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["core", "all"]
networks:
- default
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
ports:
- "51820:51820/udp"
- "$WGEASY_PORT:51821/tcp" # WebUI
volumes:
- $DOCKERDIR/appdata/wireguard:/etc/wireguard
environment:
- WG_HOST=SUBDOMAIN-PLACEHOLDER.$DOMAINNAME_1
- PASSWORD_HASH=WG-EASY-PASSWORD-HASH-PLACEHOLDER
- WG_DEFAULT_ADDRESS=$WGEASY_DEFAULT_ADDRESS
- WG_DEFAULT_DNS=$WGEASY_DEFAULT_DNS
- WG_ALLOWED_IPS=$WGEASY_ALLOWED_IPS
- UI_TRAFFIC_STATS=true
- UI_CHART_TYPE=3
# - WG_PRE_UP=echo "Pre Up" > /etc/wireguard/pre-up.txt
# - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
# - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
# - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
# DOCKER-LABELS-PLACEHOLDER
@@ -0,0 +1,32 @@
services:
# Cloudflare DDNS - Dynamic DNS Updater
ddns-updater:
image: qmcgaw/ddns-updater
container_name: ddns-updater
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["core", "all"]
networks:
- default
ports:
- "$DDNSUPDATER_PORT:8000"
volumes:
- $DOCKERDIR/appdata/ddns-updater:/updater/data # Owned by UID 1000
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
PERIOD: 12h
UPDATE_COOLDOWN_PERIOD: 5m
PUBLICIP_DNS_TIMEOUT: 3s
HTTP_TIMEOUT: 10s
# Web UI
LISTENING_PORT: 8000
# Backup
BACKUP_PERIOD: 96h # 0 to disable
BACKUP_DIRECTORY: /updater/data/backups
# Other
LOG_LEVEL: info
# SHOUTRRR_ADDRESSES: $DISCORD_SHOUTRRR_ADDRESS
# DOCKER-LABELS-PLACEHOLDER
+33
View File
@@ -0,0 +1,33 @@
services:
# Gluetun - VPN Client for Docker Containers and More
# Gluetun only for use by torrent clients + on demand lan devices.
# Arr apps do not need VPN (not recommended), unless you have ISP/country restrictions.
gluetun:
image: qmcgaw/gluetun
container_name: gluetun
security_opt:
- no-new-privileges:true
restart: unless-stopped
profiles: ["core", "all"]
networks:
- default
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
# ports:
# - $QBITTORRENTVPN_PORT:8080 # qBittorrent available at http://DOCKER-HOST-IP:$QBITTORRENTVPN_PORT
# - 8888:8888/tcp # HTTP proxy
# - 8388:8388/tcp # Shadowsocks
# - 8388:8388/udp # Shadowsocks
volumes:
- $DOCKERDIR/appdata/gluetun:/gluetun
environment:
TZ: $TZ
VPN_SERVICE_PROVIDER: $GLUETUN_VPN_SERVICE_PROVIDER
VPN_TYPE: $GLUETUN_VPN_TYPE # wireguard / openvpn
# WIREGUARD_PRIVATE_KEY: $GLUETUN_WIREGUARD_PRIVATE_KEY
# WIREGUARD_ADDRESSES: $GLUETUN_WIREGUARD_ADDRESSES
# OPENVPN_USER: $GLUETUN_OPENVPN_USERNAME
# OPENVPN_PASSWORD: $GLUETUN_OPENVPN_PASSWORD
# DOCKER-LABELS-PLACEHOLDER
+5
View File
@@ -0,0 +1,5 @@
POSTGRES_USER=n8n
POSTGRES_PASSWORD=qqWoLCgg8ElAX6
POSTGRES_DB=n8n
N8N_BASIC_AUTH_USER=mmcfetridge
N8N_BASIC_AUTH_PASSWORD=qqWoLCgg8ElAX6
@@ -0,0 +1,38 @@
services:
n8n:
image: n8nio/n8n
ports:
- "5678:5678"
container_name: n8n
env_file:
- .env
security_opt:
- no-new-privileges:true
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_USER}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=$(POSTGRES_PASSWORD)
- NODE_FUNCTION_ALLOW_EXTERNAL=axios,qs
volumes:
- ./appdata/n8n:/home/node/.n8n
depends_on:
- db
restart: unless-stopped
db:
image: postgres:12
env_file:
- .env
volumes:
- /home/miker/docker/n8n/data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=$(POSTGRES_PASSWORD)
- POSTGRES_DB=n8n
restart: unless-stopped
@@ -0,0 +1,135 @@
---
services:
gitlab:
image: docker.io/gitlab/gitlab-ce:latest
container_name: gitlab
shm_size: '256m'
environment:
PUID: 1000
PGID: 1000
volumes:
- /home/miker/docker/gitlab/config:/etc/gitlab
- /home/miker/docker/gitlab/logs:/var/log/gitlab
- /home/miker/docker/gitlab/data:/var/opt/gitlab
ports:
# --> (Optional) Remove when using traefik...
- "8080:80"
- "8443:443"
- '222:22'
restart: unless-stopped
# Lookup password assigned at install
# sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password
To use the GitLab runner in GitLab, you need to configure it. For correct configuration, we will need a token copied from the portal. To do this, go to the address: http://localhost:8080/admin/runners and click the Copy token button.
In the next step, it goes to the console and run the following command:
> docker exec -it gitlab-runner gitlab-runner register --url "http://gitlab-ce" --clone-url "http://gitlab-ce"
After launching, a configuration module will appear. The module provides the following information:
Enter the GitLab instance URL: confirm the entered value (click enter)
Enter the registration token: enter the token copied before.
Enter a description for the runner: enter the name of the runner, e.g. docker-runner
Enter tags for the runner: leave the field blank here
Enter an executor: enter docker here
Enter the default Docker image: here we provide the default docker image, e.g. maven: latest
After proper configuration, we should see confirmation Runner registred successfully:
In addition to the basic configuration, we also need to allow access for containers launched from the runner to the virtual network in which GitLab operates. To do this, we run the editor (e.g. vi)
> sudo vi gitlab/gitlab-runner/config.toml
Then we add new line to the end of the runner configuration: network_mode = “gitlab-network”
To check if the runner is available from the GitLab level, go to the following address:http://localhost:8080/admin/runners
We create our first repository
After setting up the runner, we can create our first repository. To do this, go to the page: http://localhost:8080/projects/new and click Import project.
Na następnym ekranie wybieramy Import project from: Repo from URL. Następnie podajemy w Git repository URL adress: https://github.com/czerniga/helloworld.git. Na końcu zatwierdzamy klikając Create project.
On the next screen, select Import project from: Repo from URL. Then we provide the Git repository URL address: https://github.com/czerniga/helloworld.git. Finally, confirm by clicking Create project.
After a while you should have the first repository copied to your GitLab.
We create the CI/CD pipeline
To create a CI/CD pipeline for the project, click the main menu on the left, CI/CD, and then Editor. An option to create a .gitlab-ci.yml file, which will contain our pipeline definitions, will appear on the screen. This file will be created in the Git repository.
On the new screen we can see our pipeline editor. In the editor, paste the following content:
image: maven:latest
stages:
- build
- test
build-job:
stage: build
script:
- echo "Compiling the code..."
- mvn clean package
- echo "Compile complete."
artifacts:
paths:
- target
test-job:
stage: test
dependencies:
- build-job
script:
- ls -al
- echo "Running tests"
- java -cp target/helloworld-1.1.jar com.coveros.demo.helloworld.HelloWorld
The above definition describes how the CI / CD process should work. The most important elements are:
image: docker image that we will use to build our project
stages: a list of our process steps
build-job: the first step in our process to build our project. Additionally, we save the artifacts for use in the next step
test-job: the second step to run our project
After pasting our file, confirm the changes by clicking Commit changes.
Once approved, GitLab will launch the process. To check its results, go to CI/CD -> Pipelines in the menu on the left. On the screen we should see that our first task has already been started.
We can go to the details of this task by clicking on the pending button or the build number #1
After a while, the task should be built and tested.
How to install GitLab using Docker Compose
How to install GitLab using Docker Compose
Congratulations, you have just created your first CI/CD job in GitLab!
@@ -0,0 +1,22 @@
# -- Change GitLab settings here...
external_url 'http://192.168.2.3' # <-- Replace with your GitLab FQDN
# Setting Timezone
gitlab_rails['time_zone'] = 'America/New_York'
# -- (Optional) Change GitLab Shell settings here...
gitlab_rails['gitlab_shell_ssh_port'] = 222
# -- Change internal web service settings here...
letsencrypt['enable'] = false
nginx['listen_port'] = 80
nginx['listen_https'] = false
# EMAIL SMTP Seetings
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "mail.mmcfetridge.net"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "miker@mmcfetridge.net"
gitlab_rails['smtp_password'] = "!Sucyetat123"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
+16
View File
@@ -0,0 +1,16 @@
MYSQL_ROOT_PASSWORD=Lzh8CMp3K0c84n
MYSQL_DATABASE=gitea
MYSQL_USER=gitea
MYSQL_PASSWORD=Lzh8CMp3K0c84n
GITEA_APP_NAME=Gitea
GITEA__database__DB_TYPE=mysql
GITEA__database__HOST=db:3306
GITEA__database__NAME=gitea
GITEA__database__USER=gitea
GITEA__database__PASSWD=VEoE4sTtt8oQqR47rtGkcry4HKYtcOZU
GITEA_INSTANCE_URL=https://gitea.mikemcfetridge.com
GITEA_RUNNER_REGISTRATION_TOKEN=skQ2WOtD22Oy0g
GITEA_RUNNER_NAME=local-runner
GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-bullseye
@@ -0,0 +1,128 @@
Gitea Install Guide
Okay, let's break down the user's request first. They want me to explain a Docker Compose file and provide installation steps. The provided context shows two different Docker Compose configurations - one for Gitea with MySQL database using standard images, and another for MariaDB with npm-db service.
Hmm, interesting that they're asking about both files though the user didn't explicitly mention which one. Looking at Context 0: It's a Gitea setup file with three services (db, gitea, runner) all in a bridge network called 'gitea'. This seems to be for setting up Git hosting services locally.
Context 1 appears different - it mentions npm-db and MariaDB with volume './data/mysql:/var/lib/mysql', which suggests this might be for Node.js related projects. The user didn't specify which file they're referring to though.
The user's follow-up question is actually quite broad since we don't know exactly which service configuration they need help with. They want both an explanation and installation instructions, but without knowing the exact use case (Git server vs database setup), I can only focus on the first context provided about Gitea.
I should explain that while they requested one docker-compose file, two different configurations were provided in the contexts - one for Git hosting services and another possibly related to npm with MariaDB. Since we don't know which was originally intended, I'll focus on explaining the available information (the Gitea configuration) as much as possible.
The user seems technically proficient enough to handle Docker setups but might be looking for clarification about how specific components interact in this environment. They're likely setting up some development or testing infrastructure and need guidance.
</think>
Okay, here is a technical explanation of the `docker-compose.yml` content provided (Context 0), followed by step-by-step instructions on how to use it.
**Technical Explanation**
This Docker Compose file (`docker-compose.yml`) defines three distinct services within named networks called "gitea":
1. **Database Service (`db`):**
* Uses the official MySQL image version 8.
* Container Name: `gitea-db`
* Restart Policy: `unless-stopped` - The container will automatically restart if it stops, unless explicitly stopped by an administrator command or Docker daemon rules (like on shutdown).
* Environment Variables: Loaded from a `.env` file and sets standard database credentials (`MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE`, etc.).
* Volumes: Mounts the host's current directory (`./mysql`) into the container's default MySQL data location (`/var/lib/mysql`). This stores the Dockerized database files on your host machine for persistence.
* Networks: Connects to the "gitea" bridge network so other services can communicate with it via its internal name ("db").
* Healthcheck: Uses `mysqladmin ping` command against the local MySQL server (`localhost`) using user and password from environment variables. Checks occur every 10 seconds, wait up to 5s for a response, retry 5 times (total 50s). If it fails, the service is marked unhealthy.
2. **Gitea Service (`gitea`):**
* Uses the official Gitea image from Docker Hub (`gitea/gitea:latest`). This sets up a Git server.
* Container Name: `gitea`
* Restart Policy: `unless-stopped` - Same behavior as the database service regarding automatic restarts.
* Environment Variables: Loaded from `.env`. It requires user ID and group ID (`USER_UID=1000`, `USER_GID=1000`) for proper file ownership within its data volume. The crucial Gitea configuration variables are set here, pointing it to the database server ("db"), specifying the DB type (likely "mysql", loaded from `.env`), and providing credentials (`GITEA__database__...`). It also sets a higher priority port mapping for access.
* Volumes: Mounts two directories on the host:
- `./gitea:/data`: Stores Gitea's application data (repositories, configuration files, logs) in `/data` within the container. Host directory is `./gitea`.
- `/etc/localtime:/etc/localtime:ro`: Makes this container use your host system's timezone setting.
* Networks: Connects to the "gitea" bridge network.
* Depends On: The service definition includes a condition (`service_healthy`) that ensures it waits for the `db` service (named dependency) to pass its health checks before starting. Gitea itself uses an internal healthcheck via `curl`.
* Ports:
- `"3000:3000"`: Exposes Gitea's web UI port from container 3000 to host 3000.
- `"222:22"`: Exposes the SSH server port from container 22 (default for Docker image) to host 222. This allows accessing Git repositories via SSH clients.
3. **Gitea Runner Service (`runner`):**
* Uses a dedicated Gitea CI runner image (`gitea/act_runner:latest`).
* Container Name: `gitea-runner`
* Restart Policy: `unless-stopped`.
* Depends On: Will wait for the `gitea` service (named dependency) to be healthy before starting.
* Environment Variables: Loaded from `.env`. Key variables include Gitea's instance URL (`GITEA_INSTANCE_URL`) and a registration token (`GITEA_RUNNER_REGISTRATION_TOKEN`). It also specifies its name, labels for job routing, and log level (trace). Crucially, it sets the `GITEA_RUNNER_JOB_CONTAINER_NETWORK` to "gitea".
* Volumes: Mounts three directories:
- `./runner-data:/data`: Stores CI artifacts or runner-specific data in a subdirectory of Gitea's main directory.
- `/config.yaml`: This line seems incorrect as provided. Assuming it means mounting the host-side generated configuration file (which is typically done elsewhere) but based on the pattern, it should likely be `- ./runner-config.yaml:/config.yaml`. The `act_runner` *needs* this config file internally, so providing one via volume mount from `./runner-config.yaml` would configure it appropriately. This directory (`./runner-data`) exists and holds persistent data for jobs.
- `/var/run/docker.sock:/var/run/docker.sock`: Mounts the host's Docker socket into this container. The Gitea runner executes CI commands within a separate Docker container (using `act`). Access to the host's Docker socket is necessary for it to perform these actions (`docker run ...`).
**Common Elements:**
* `.env`: Used by multiple services to load environment variables, likely containing secrets and configuration values.
* `gitea` network: A bridge network connecting all three services. They communicate via their internal names (like `db`, `gitea`, `runner`) on this network.
**Purpose:** This setup creates a self-contained local instance of Gitea and its CI runner, with an accompanying MySQL database. It's useful for testing Git functionalities locally or setting up a personal/private Git server without needing to install the software directly on the host OS.
---
**Step-by-Step Installation Guide (Based on Context 0)**
This guide assumes you want to set up Gitea and its runner using Docker Compose with a MySQL database. The instructions are for this specific configuration file structure shown in Context 0.
1. **Prerequisites:**
* Ensure your host machine meets the [requirements](https://docs.gitea.com/installation/requirements) for running Gitea.
* Install [Docker Engine](https://docs.docker.com/engine/install/) and [Docker Compose V2](https://docs.docker.com/compose/v2/) on your system (Windows, macOS, Linux).
* Have a `.env` file ready or create one. This file should contain the necessary environment variables for Gitea and MySQL.
* Example contents of your `.env` file might include:
```
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_DATABASE=gitea_db_name # Name for the database used by Gitea
MYSQL_USER=mysql_user # Database user (Gitea)
MYSQL_PASSWORD=mysql_password # Password for that user
GITEA_APP_NAME=Gitea_Instance_Name # Can be set here or in .env file, but also might default to image name.
GITEA__database__DB_TYPE=mysql
GITEA__database__HOST=db:3306 # "db" is the internal hostname for the MySQL service (from context)
GITEA__database__NAME=gitea_db_name # Same as MYSQL_DATABASE above, *or* this might override it. Check consistency.
GITEA__database__USER=mysql_user # Same as MYSQL_USER
GITEA__database__PASSWD=mysql_password # Same as MYSQL_PASSWORD
# For the runner (optional if you don't need CI)
GITEA_INSTANCE_URL=http://localhost:3000 # Should match your host mapping for gitea service (typically :3000)
GITEA_RUNNER_REGISTRATION_TOKEN=your_token
```
2. **Prepare the Directory Structure:**
* Create a directory where you want to run this setup.
* Copy both `docker-compose.yml` and `.env` files into that directory from wherever they are located (ensure Context 0's `docker-compose.yml` is used).
* Example structure:
```
/path/to/my/gitea/installation/
├── docker-compose.yml
├── .env
├── gitea/ # Will be created by the first run. Contains Gitea data (repositories, logs)
└── runner-data/ # Will be created by the Gitea runner container when it starts for the first time.
```
* Alternatively, if you are using a single `docker-compose.yml` file from Context 0 and want to manage other services later, ensure your chosen directory is correct.
3. **Start the Services:**
* Open a terminal or command prompt in the directory containing `docker-compose.yml`.
* Run the following command:
```
docker compose up -d
```
This command starts all three defined services (`db`, `gitea`, and `runner`) in detached mode (background).
4. **Verify Service Startup:**
* Check if the database service is healthy first, as it's critical for Gitea:
```
docker compose ps # Lists running containers
docker compose inspect gitea-db | grep -A10 'healthcheck.*Status' # Inspect container details to see health status (requires Docker Compose V2)
```
* Check the logs of all services if necessary for startup errors:
```
docker compose logs # Shows logs from all services
# Or specific one, e.g., `docker compose logs gitea` or `docker compose logs runner`
```
5. **Accessing Gitea:**
* Open a web browser and navigate to `http://localhost:3000`. The default username is `admin`, password is `root`.
* You might see an initial setup page if this is the first run.
**Note:** This configuration maps the SSH port (22 for container) to host 222. Remember that ports below 1024 often require root privileges on Linux systems, but Docker Compose handles these mappings appropriately regardless of OS usually.
@@ -0,0 +1,118 @@
---
networks:
internal:
name: internal
driver: bridge
external:
name: external
driver: bridge
services:
# -------------------------------------------------------------------
# 1. THE REPOSITORY: Gitea Backend
# -------------------------------------------------------------------
db:
image: mysql:8
container_name: gitea-db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
volumes:
- ./mysql:/var/lib/mysql
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u${MYSQL_USER} -p${MYSQL_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
# -------------------------------------------------------------------
# 2. THE REPOSITORY: Gitea Frontend
# -------------------------------------------------------------------
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
env_file: .env
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__SSH_PORT=222
- GITEA__server__SSH_LISTEN_PORT=22
- GITEA__server__SSH_DOMAIN=mygitea.mikemcfetridge.com
- GITEA_APP_NAME=${GITEA_APP_NAME}
- GITEA__database__DB_TYPE=${GITEA__database__DB_TYPE}
- GITEA__database__HOST=${GITEA__database__HOST}
- GITEA__database__NAME=${GITEA__database__NAME}
- GITEA__database__USER=${GITEA__database__USER}
- GITEA__database__PASSWD=${GITEA__database__PASSWD}
depends_on:
db:
condition: service_healthy
ports:
- "3000:3000"
- "222:22"
volumes:
- ./gitea:/data
- /etc/localtime:/etc/localtime:ro
networks:
- internal
- external
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 10s
retries: 3
timeout: 10s
start_period: 30s
# -------------------------------------------------------------------
# 3. THE GITEA runner using docker compose
# -------------------------------------------------------------------
runner:
image: gitea/act_runner:latest
container_name: gitea-runner
restart: unless-stopped
depends_on:
gitea:
condition: service_healthy
env_file: .env
environment:
- GITEA_INSTANCE_URL=${GITEA_INSTANCE_URL}
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_REGISTRATION_TOKEN}
- GITEA_RUNNER_NAME=${GITEA_RUNNER_NAME}
- GITEA_RUNNER_LABELS=${GITEA_RUNNER_LABELS}
- GITEA_RUNNER_LOG_LEVEL=trace
- GITEA_RUNNER_JOB_CONTAINER_NETWORK=external
volumes:
- ./runner-data:/data
- ./runner-config.yaml:/config.yaml
- /var/run/docker.sock:/var/run/docker.sock
networks:
- internal
- external
# -------------------------------------------------------------------
# 4. THE SECURITY LAYER: Docker Socket Proxy
# -------------------------------------------------------------------
docker-proxy:
image: tecnativa/docker-socket-proxy:latest
container_name: docker_proxy
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- NETWORKS=1
- SERVICES=1
- VOLUMES=1
- POST=1 # Allows Semaphore to create/start components
networks:
- external
restart: unless-stopped

Some files were not shown because too many files have changed in this diff Show More