Grow the bundled template library to 83 homelab apps (0.43.0)
CI / build-and-push (push) Successful in 1m46s

The library shipped five templates. This adds 78 more, covering what
homelab lists and the self-hosted community actually run: media servers
and the *arr automation chain, DNS ad-blocking, reverse proxies, VPN,
SSO, monitoring and dashboards, files/backup, notes and wikis, home
automation, dev tooling, databases, local AI, finance and notifications.

Every template follows the existing shape — compose.yaml, .env.example,
template.json — with PUID/PGID/TZ/DATA_PATH/HTTP_PORT knobs and no
literal secrets: anything that must be set uses ${VAR:?...} so deploy
fails loudly instead of coming up with a default password. Six ship the
extra config file their app needs (prometheus.yml, Caddyfile,
mosquitto.conf, frigate config.yml, Authelia's two files,
zigbee2mqtt configuration.yaml), which the folder-copy pull already
carries into the new stack.

All 95 referenced images were verified pullable against their
registries. Default host ports were deconflicted so several templates
can be pulled side by side; the only remaining overlaps are between
services you would never run together anyway (two DNS blockers on 53,
three reverse proxies on 80/443).

The Templates page would have been an unusable 83-card grid, so it now
has a search box and tag filter chips, with the tags on each card
clickable to filter by.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dk43rmEeRfYi5wsLDbmfyG
This commit is contained in:
menzelj
2026-08-31 09:12:34 +02:00
co-authored by Claude Opus 5
parent 2afec08c4f
commit b3af0c2109
245 changed files with 2963 additions and 14 deletions
+9 -5
View File
@@ -76,11 +76,15 @@ as intuitive as Dockge, as capable as Portainer for Compose workflows.
(`/proc/net/tcp[6]`) and running container bindings, with a confirm dialog.
- **Resource limits**: CPU/memory sliders in the editor → `deploy.resources.limits`.
- **Template library**: each template is a ready-to-run **stack folder** in git
(`backend/templates/<slug>/` — `compose.yaml` + `.env.example` + `template.json`).
Bundled set: Jellyfin, Vaultwarden, Uptime-Kuma, Paperless-NGX, Gitea. "Pull"
copies the whole folder into a new stack (`.env.example` → `.env`) which you then
edit and deploy. Save any stack back as a custom template (stored under
`${DATA_DIR}/templates/`). Add your own by dropping a folder into the templates dir.
(`backend/templates/<slug>/` — `compose.yaml` + `.env.example` + `template.json`,
plus any extra config the app needs, e.g. `prometheus.yml` or a `Caddyfile`).
83 bundled homelab apps across media, *arr automation, networking, reverse
proxies, VPN, SSO, monitoring, dashboards, files/backup, notes and wikis, home
automation, dev tooling, databases, local AI and more — searchable and
filterable by tag on the Templates page. "Pull" copies the whole folder into a
new stack (`.env.example` → `.env`) which you then edit and deploy. Save any
stack back as a custom template (stored under `${DATA_DIR}/templates/`). Add
your own by dropping a folder into the templates dir.
- **Healthcheck status** surfaced per container in the stack overview.
### Phase 4 — Operations
@@ -0,0 +1,3 @@
TZ=Europe/Berlin
DATA_PATH=/srv/actual
HTTP_PORT=5006
@@ -0,0 +1,11 @@
services:
actual:
image: ghcr.io/actualbudget/actual-server:latest
container_name: actual-budget
restart: unless-stopped
environment:
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-5006}:5006"
volumes:
- ${DATA_PATH:-/srv/actual}:/data
@@ -0,0 +1,9 @@
{
"name": "Actual Budget",
"description": "Local-first envelope budgeting with end-to-end encrypted sync across devices.",
"tags": [
"finance",
"productivity"
],
"gpu": null
}
@@ -0,0 +1,4 @@
DATA_PATH=/srv/adguardhome
DNS_PORT=53
SETUP_PORT=3300
HTTP_PORT=8082
@@ -0,0 +1,14 @@
services:
adguardhome:
image: adguard/adguardhome:latest
container_name: adguardhome
restart: unless-stopped
ports:
- "${DNS_PORT:-53}:53/tcp"
- "${DNS_PORT:-53}:53/udp"
# Setup wizard on first run; the UI moves to HTTP_PORT afterwards.
- "${SETUP_PORT:-3300}:3000/tcp"
- "${HTTP_PORT:-8082}:80/tcp"
volumes:
- ${DATA_PATH:-/srv/adguardhome}/work:/opt/adguardhome/work
- ${DATA_PATH:-/srv/adguardhome}/conf:/opt/adguardhome/conf
@@ -0,0 +1,10 @@
{
"name": "AdGuard Home",
"description": "DNS server with ad and tracker blocking, DoH/DoT and per-client rules.",
"tags": [
"network",
"dns",
"ad-blocking"
],
"gpu": null
}
@@ -0,0 +1,5 @@
TZ=Europe/Berlin
DATA_PATH=/srv/audiobookshelf
AUDIOBOOKS_PATH=/srv/media/audiobooks
PODCASTS_PATH=/srv/media/podcasts
HTTP_PORT=13378
@@ -0,0 +1,14 @@
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
restart: unless-stopped
environment:
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-13378}:80"
volumes:
- ${DATA_PATH:-/srv/audiobookshelf}/config:/config
- ${DATA_PATH:-/srv/audiobookshelf}/metadata:/metadata
- ${AUDIOBOOKS_PATH:-/srv/media/audiobooks}:/audiobooks
- ${PODCASTS_PATH:-/srv/media/podcasts}:/podcasts
@@ -0,0 +1,10 @@
{
"name": "Audiobookshelf",
"description": "Audiobook and podcast server that keeps progress in sync across devices.",
"tags": [
"media",
"books",
"streaming"
],
"gpu": null
}
+3
View File
@@ -0,0 +1,3 @@
TZ=Europe/Berlin
DATA_PATH=/srv/authelia
HTTP_PORT=9091
+13
View File
@@ -0,0 +1,13 @@
services:
authelia:
image: authelia/authelia:latest
container_name: authelia
restart: unless-stopped
environment:
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-9091}:9091"
volumes:
- ./configuration.yml:/config/configuration.yml:ro
- ./users_database.yml:/config/users_database.yml
- ${DATA_PATH:-/srv/authelia}:/config/db
@@ -0,0 +1,34 @@
# Minimal Authelia config. Replace every "change-me" and the example domains.
theme: dark
identity_validation:
reset_password:
jwt_secret: change-me-jwt-secret
server:
address: tcp://0.0.0.0:9091
authentication_backend:
file:
path: /config/users_database.yml
access_control:
default_policy: deny
rules:
- domain: "*.example.com"
policy: two_factor
session:
secret: change-me-session-secret
cookies:
- domain: example.com
authelia_url: https://auth.example.com
storage:
encryption_key: change-me-encryption-key-at-least-20-chars
local:
path: /config/db/db.sqlite3
notifier:
filesystem:
filename: /config/db/notification.txt
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Authelia",
"description": "Lightweight authentication and 2FA portal for reverse-proxy forward auth.",
"tags": [
"security",
"identity",
"sso"
],
"gpu": null
}
@@ -0,0 +1,10 @@
# Generate a password hash with:
# docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'yourpassword'
users:
admin:
disabled: false
displayname: "Admin"
password: "$argon2id$v=19$m=65536,t=3,p=4$REPLACE_ME"
email: admin@example.com
groups:
- admins
+7
View File
@@ -0,0 +1,7 @@
DATA_PATH=/srv/authentik
AUTHENTIK_TAG=2026.8.0
HTTP_PORT=9200
HTTPS_PORT=9243
# Both required. Generate with: openssl rand -base64 36
PG_PASS=change-me
AUTHENTIK_SECRET_KEY=change-me
+61
View File
@@ -0,0 +1,61 @@
services:
postgresql:
image: postgres:16-alpine
container_name: authentik-db
restart: unless-stopped
environment:
- POSTGRES_DB=authentik
- POSTGRES_USER=authentik
- POSTGRES_PASSWORD=${PG_PASS:?database password required}
healthcheck:
test: ["CMD-SHELL", "pg_isready -d authentik -U authentik"]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
volumes:
- ${DATA_PATH:-/srv/authentik}/database:/var/lib/postgresql/data
server:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2026.8.0}
container_name: authentik-server
command: server
restart: unless-stopped
shm_size: 512mb
depends_on:
postgresql:
condition: service_healthy
environment:
- AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY:?secret key required}
- AUTHENTIK_POSTGRESQL__HOST=postgresql
- AUTHENTIK_POSTGRESQL__NAME=authentik
- AUTHENTIK_POSTGRESQL__USER=authentik
- AUTHENTIK_POSTGRESQL__PASSWORD=${PG_PASS}
ports:
- "${HTTP_PORT:-9200}:9000"
- "${HTTPS_PORT:-9243}:9443"
volumes:
- ${DATA_PATH:-/srv/authentik}/data:/data
- ${DATA_PATH:-/srv/authentik}/custom-templates:/templates
worker:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2026.8.0}
container_name: authentik-worker
command: worker
restart: unless-stopped
shm_size: 512mb
user: root
depends_on:
postgresql:
condition: service_healthy
environment:
- AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
- AUTHENTIK_POSTGRESQL__HOST=postgresql
- AUTHENTIK_POSTGRESQL__NAME=authentik
- AUTHENTIK_POSTGRESQL__USER=authentik
- AUTHENTIK_POSTGRESQL__PASSWORD=${PG_PASS}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${DATA_PATH:-/srv/authentik}/data:/data
- ${DATA_PATH:-/srv/authentik}/certs:/certs
- ${DATA_PATH:-/srv/authentik}/custom-templates:/templates
+10
View File
@@ -0,0 +1,10 @@
{
"name": "authentik",
"description": "Identity provider and SSO gateway (OAuth2, SAML, LDAP, forward auth). Finish setup at /if/flow/initial-setup/.",
"tags": [
"security",
"identity",
"sso"
],
"gpu": null
}
+6
View File
@@ -0,0 +1,6 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/bazarr
MEDIA_PATH=/srv/media
HTTP_PORT=6767
+14
View File
@@ -0,0 +1,14 @@
services:
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-6767}:6767"
volumes:
- ${DATA_PATH:-/srv/bazarr}:/config
- ${MEDIA_PATH:-/srv/media}:/media
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Bazarr",
"description": "Companion to Sonarr and Radarr that downloads matching subtitles.",
"tags": [
"media",
"automation",
"arr"
],
"gpu": null
}
+4
View File
@@ -0,0 +1,4 @@
DATA_PATH=/srv/beszel
HTTP_PORT=8090
AGENT_PORT=45876
AGENT_KEY=
+22
View File
@@ -0,0 +1,22 @@
services:
beszel:
image: henrygd/beszel:latest
container_name: beszel
restart: unless-stopped
ports:
- "${HTTP_PORT:-8090}:8090"
volumes:
- ${DATA_PATH:-/srv/beszel}/data:/beszel_data
beszel-agent:
image: henrygd/beszel-agent:latest
container_name: beszel-agent
restart: unless-stopped
network_mode: host
environment:
- LISTEN=${AGENT_PORT:-45876}
# Copy this from the "add system" dialog in the Beszel UI.
- KEY=${AGENT_KEY:-}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${DATA_PATH:-/srv/beszel}/agent:/var/lib/beszel-agent
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Beszel",
"description": "Lightweight server monitoring with historical charts, alerts and Docker stats.",
"tags": [
"monitoring",
"metrics",
"docker"
],
"gpu": null
}
+10
View File
@@ -0,0 +1,10 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/bookstack
HTTP_PORT=6875
APP_URL=http://localhost:6875
# Required, must be "base64:..." — generate with: echo "base64:$(openssl rand -base64 32)"
APP_KEY=
DB_PASSWORD=change-me
DB_ROOT_PASSWORD=change-me
+38
View File
@@ -0,0 +1,38 @@
services:
bookstack:
image: lscr.io/linuxserver/bookstack:latest
container_name: bookstack
restart: unless-stopped
depends_on:
- bookstack-db
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
- APP_URL=${APP_URL:-http://localhost:6875}
# Required. Generate with: openssl rand -base64 32
- APP_KEY=${APP_KEY:?app key required}
- DB_HOST=bookstack-db
- DB_PORT=3306
- DB_DATABASE=bookstackapp
- DB_USERNAME=bookstack
- DB_PASSWORD=${DB_PASSWORD:-bookstack}
ports:
- "${HTTP_PORT:-6875}:80"
volumes:
- ${DATA_PATH:-/srv/bookstack}/config:/config
bookstack-db:
image: lscr.io/linuxserver/mariadb:latest
container_name: bookstack-db
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-bookstack}
- MYSQL_DATABASE=bookstackapp
- MYSQL_USER=bookstack
- MYSQL_PASSWORD=${DB_PASSWORD:-bookstack}
volumes:
- ${DATA_PATH:-/srv/bookstack}/db:/config
+10
View File
@@ -0,0 +1,10 @@
{
"name": "BookStack",
"description": "Documentation platform organised into shelves, books, chapters and pages. First login: admin@admin.com / password.",
"tags": [
"documents",
"wiki",
"productivity"
],
"gpu": null
}
+3
View File
@@ -0,0 +1,3 @@
DATA_PATH=/srv/caddy
HTTP_PORT=80
HTTPS_PORT=443
+11
View File
@@ -0,0 +1,11 @@
# Caddy issues and renews TLS certificates automatically for any real hostname.
# Replace the examples below with your own, then restart the stack.
app.example.com {
reverse_proxy host.docker.internal:8080
}
# Local-only site on plain HTTP (no certificate needed):
# http://nas.lan {
# reverse_proxy 192.168.1.10:5000
# }
+13
View File
@@ -0,0 +1,13 @@
services:
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "${HTTP_PORT:-80}:80"
- "${HTTPS_PORT:-443}:443"
- "${HTTPS_PORT:-443}:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ${DATA_PATH:-/srv/caddy}/data:/data
- ${DATA_PATH:-/srv/caddy}/config:/config
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Caddy",
"description": "Reverse proxy with automatic HTTPS, configured through a short Caddyfile.",
"tags": [
"network",
"proxy"
],
"gpu": null
}
@@ -0,0 +1,6 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/calibre-web
LIBRARY_PATH=/srv/media/books
HTTP_PORT=8083
@@ -0,0 +1,16 @@
services:
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
container_name: calibre-web
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
# Adds ebook format conversion (downloads Calibre on first start)
- DOCKER_MODS=linuxserver/mods:universal-calibre
ports:
- "${HTTP_PORT:-8083}:8083"
volumes:
- ${DATA_PATH:-/srv/calibre-web}:/config
- ${LIBRARY_PATH:-/srv/media/books}:/books
@@ -0,0 +1,9 @@
{
"name": "Calibre-Web",
"description": "Web front-end for a Calibre ebook library, with OPDS and send-to-Kindle.",
"tags": [
"media",
"books"
],
"gpu": null
}
@@ -0,0 +1,3 @@
DATA_PATH=/srv/changedetection
HTTP_PORT=5000
BASE_URL=http://localhost:5000
@@ -0,0 +1,25 @@
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
environment:
- BASE_URL=${BASE_URL:-http://localhost:5000}
- PLAYWRIGHT_DRIVER_URL=ws://browser:3000
ports:
- "${HTTP_PORT:-5000}:5000"
volumes:
- ${DATA_PATH:-/srv/changedetection}:/datastore
depends_on:
- browser
browser:
image: dgtlmoon/sockpuppetbrowser:latest
container_name: changedetection-browser
restart: unless-stopped
cap_add:
- SYS_ADMIN
environment:
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1024
- MAX_CONCURRENT_CHROME_PROCESSES=10
@@ -0,0 +1,9 @@
{
"name": "changedetection.io",
"description": "Watches web pages for changes and notifies you — with a headless browser for JS-heavy sites.",
"tags": [
"monitoring",
"automation"
],
"gpu": null
}
@@ -0,0 +1,9 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/code-server
PROJECTS_PATH=/srv/projects
HTTP_PORT=8443
# Required — this is the web login password.
PASSWORD=
SUDO_PASSWORD=
@@ -0,0 +1,17 @@
services:
code-server:
image: lscr.io/linuxserver/code-server:latest
container_name: code-server
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
- PASSWORD=${PASSWORD:?password required}
- SUDO_PASSWORD=${SUDO_PASSWORD:-}
- DEFAULT_WORKSPACE=/config/workspace
ports:
- "${HTTP_PORT:-8443}:8443"
volumes:
- ${DATA_PATH:-/srv/code-server}:/config
- ${PROJECTS_PATH:-/srv/projects}:/config/workspace
@@ -0,0 +1,8 @@
{
"name": "code-server",
"description": "VS Code in the browser, running on the server where your files already are.",
"tags": [
"dev"
],
"gpu": null
}
+2
View File
@@ -0,0 +1,2 @@
DATA_PATH=/srv/dashy
HTTP_PORT=4000
+12
View File
@@ -0,0 +1,12 @@
services:
dashy:
image: lissy93/dashy:latest
container_name: dashy
restart: unless-stopped
environment:
- NODE_ENV=production
ports:
- "${HTTP_PORT:-4000}:8080"
volumes:
# Dashy writes conf.yml back into this directory when you edit in the UI.
- ${DATA_PATH:-/srv/dashy}:/app/user-data
+8
View File
@@ -0,0 +1,8 @@
{
"name": "Dashy",
"description": "Highly themeable dashboard with status checks, editable from the browser.",
"tags": [
"dashboard"
],
"gpu": null
}
@@ -0,0 +1,4 @@
DATA_PATH=/srv/registry
HTTP_PORT=5050
UI_PORT=5080
REGISTRY_TITLE=Homelab Registry
@@ -0,0 +1,29 @@
services:
registry:
image: registry:2
container_name: registry
restart: unless-stopped
environment:
- REGISTRY_STORAGE_DELETE_ENABLED=true
ports:
- "${HTTP_PORT:-5050}:5000"
volumes:
- ${DATA_PATH:-/srv/registry}:/var/lib/registry
# Basic auth. Create it with:
# docker run --rm httpd:alpine htpasswd -Bbn user pass > htpasswd
# then uncomment the mount and the three REGISTRY_AUTH_* vars above.
# - ./htpasswd:/auth/htpasswd:ro
registry-ui:
image: joxit/docker-registry-ui:latest
container_name: registry-ui
restart: unless-stopped
depends_on:
- registry
environment:
- SINGLE_REGISTRY=true
- REGISTRY_TITLE=${REGISTRY_TITLE:-Homelab Registry}
- NGINX_PROXY_PASS_URL=http://registry:5000
- DELETE_IMAGES=true
ports:
- "${UI_PORT:-5080}:80"
@@ -0,0 +1,9 @@
{
"name": "Docker Registry",
"description": "Private container registry with a browsable web UI. Add auth and TLS before exposing it.",
"tags": [
"docker",
"dev"
],
"gpu": null
}
+3
View File
@@ -0,0 +1,3 @@
DATA_PATH=/srv/dockge
STACKS_DIR=/opt/stacks
HTTP_PORT=5001
+14
View File
@@ -0,0 +1,14 @@
services:
dockge:
image: louislam/dockge:1
container_name: dockge
restart: unless-stopped
environment:
- DOCKGE_STACKS_DIR=${STACKS_DIR:-/opt/stacks}
ports:
- "${HTTP_PORT:-5001}:5001"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${DATA_PATH:-/srv/dockge}:/app/data
# Host path and container path must match, or compose lookups break.
- ${STACKS_DIR:-/opt/stacks}:${STACKS_DIR:-/opt/stacks}
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Dockge",
"description": "Compose-stack manager with a live editor and terminal output.",
"tags": [
"docker",
"dashboard"
],
"gpu": null
}
+1
View File
@@ -0,0 +1 @@
HTTP_PORT=8888
+12
View File
@@ -0,0 +1,12 @@
services:
dozzle:
image: amir20/dozzle:latest
container_name: dozzle
restart: unless-stopped
environment:
- DOZZLE_LEVEL=info
- DOZZLE_NO_ANALYTICS=true
ports:
- "${HTTP_PORT:-8888}:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Dozzle",
"description": "Live container log viewer in the browser — no log shipping, no storage.",
"tags": [
"monitoring",
"docker",
"logs"
],
"gpu": null
}
+10
View File
@@ -0,0 +1,10 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/duplicati
BACKUPS_PATH=/srv/backups
SOURCE_PATH=/srv
HTTP_PORT=8201
WEB_PASSWORD=change-me
# Required. Generate with: openssl rand -hex 32
SETTINGS_ENCRYPTION_KEY=
+17
View File
@@ -0,0 +1,17 @@
services:
duplicati:
image: lscr.io/linuxserver/duplicati:latest
container_name: duplicati
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
- SETTINGS_ENCRYPTION_KEY=${SETTINGS_ENCRYPTION_KEY:?encryption key required}
- DUPLICATI__WEBSERVICE_PASSWORD=${WEB_PASSWORD:-change-me}
ports:
- "${HTTP_PORT:-8201}:8200"
volumes:
- ${DATA_PATH:-/srv/duplicati}:/config
- ${BACKUPS_PATH:-/srv/backups}:/backups
- ${SOURCE_PATH:-/srv}:/source:ro
@@ -0,0 +1,9 @@
{
"name": "Duplicati",
"description": "Scheduled, encrypted, deduplicated backups to local disks or any cloud target.",
"tags": [
"backup",
"files"
],
"gpu": null
}
+6
View File
@@ -0,0 +1,6 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
CONFIG_PATH=/srv/emby/config
MEDIA_PATH=/srv/media
HTTP_PORT=8105
+17
View File
@@ -0,0 +1,17 @@
services:
emby:
image: lscr.io/linuxserver/emby:latest
container_name: emby
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-8105}:8096"
volumes:
- ${CONFIG_PATH:-/srv/emby/config}:/config
- ${MEDIA_PATH:-/srv/media}:/media
# Uncomment for Intel/AMD hardware transcoding (Emby Premiere required):
# devices:
# - /dev/dri:/dev/dri
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Emby",
"description": "Media server for movies, TV and music, with live TV and DVR support.",
"tags": [
"media",
"streaming"
],
"gpu": "NVIDIA / Intel optional"
}
+6
View File
@@ -0,0 +1,6 @@
TZ=Europe/Berlin
DATA_PATH=/srv/esphome
# Optional basic auth for the dashboard.
USERNAME=
PASSWORD=
# Runs on host networking — the UI is on port 6052 of the host itself.
+15
View File
@@ -0,0 +1,15 @@
services:
esphome:
image: ghcr.io/esphome/esphome:latest
container_name: esphome
restart: unless-stopped
# Host networking is needed for OTA discovery of your devices.
network_mode: host
privileged: true
environment:
- TZ=${TZ:-Europe/Berlin}
- USERNAME=${USERNAME:-}
- PASSWORD=${PASSWORD:-}
volumes:
- ${DATA_PATH:-/srv/esphome}:/config
- /etc/localtime:/etc/localtime:ro
+10
View File
@@ -0,0 +1,10 @@
{
"name": "ESPHome",
"description": "Build and flash firmware for ESP32/ESP8266 sensors straight from the browser.",
"tags": [
"home-automation",
"iot",
"dev"
],
"gpu": null
}
@@ -0,0 +1,5 @@
PUID=1000
PGID=1000
DATA_PATH=/srv/filebrowser
FILES_PATH=/srv
HTTP_PORT=8088
@@ -0,0 +1,16 @@
services:
filebrowser:
# Pinned to the v2 line — v3 changes the on-disk layout.
image: filebrowser/filebrowser:v2
container_name: filebrowser
restart: unless-stopped
user: "${PUID:-1000}:${PGID:-1000}"
environment:
- FB_ROOT=/srv
- FB_DATABASE=/database/filebrowser.db
- FB_PORT=80
ports:
- "${HTTP_PORT:-8088}:80"
volumes:
- ${FILES_PATH:-/srv}:/srv
- ${DATA_PATH:-/srv/filebrowser}/database:/database
@@ -0,0 +1,8 @@
{
"name": "File Browser",
"description": "Web file manager for a directory tree. Default login admin/admin — change it immediately.",
"tags": [
"files"
],
"gpu": null
}
@@ -0,0 +1,7 @@
TZ=Europe/Berlin
DATA_PATH=/srv/firefly-iii
HTTP_PORT=8104
APP_URL=http://localhost:8104
# Required, exactly 32 characters. Generate with: openssl rand -hex 16
APP_KEY=
DB_PASSWORD=change-me
@@ -0,0 +1,37 @@
services:
app:
image: fireflyiii/core:latest
container_name: firefly-iii
restart: unless-stopped
depends_on:
- db
environment:
- TZ=${TZ:-Europe/Berlin}
- APP_ENV=production
- APP_URL=${APP_URL:-http://localhost:8095}
# Required, exactly 32 characters. Generate with: openssl rand -hex 16
- APP_KEY=${APP_KEY:?app key required, exactly 32 characters}
- TRUSTED_PROXIES=**
- DB_CONNECTION=mysql
- DB_HOST=db
- DB_PORT=3306
- DB_DATABASE=firefly
- DB_USERNAME=firefly
- DB_PASSWORD=${DB_PASSWORD:-firefly}
ports:
- "${HTTP_PORT:-8104}:8080"
volumes:
- ${DATA_PATH:-/srv/firefly-iii}/upload:/var/www/html/storage/upload
db:
image: mariadb:11
container_name: firefly-iii-db
restart: unless-stopped
environment:
- MARIADB_AUTO_UPGRADE=1
- MYSQL_DATABASE=firefly
- MYSQL_USER=firefly
- MYSQL_PASSWORD=${DB_PASSWORD:-firefly}
- MYSQL_RANDOM_ROOT_PASSWORD=yes
volumes:
- ${DATA_PATH:-/srv/firefly-iii}/db:/var/lib/mysql
@@ -0,0 +1,9 @@
{
"name": "Firefly III",
"description": "Double-entry personal finance manager with budgets, rules and reports.",
"tags": [
"finance",
"productivity"
],
"gpu": null
}
+7
View File
@@ -0,0 +1,7 @@
PUID=1000
PGID=1000
DATA_PATH=/srv/forgejo
HTTP_PORT=3301
SSH_PORT=2223
ROOT_URL=http://localhost:3000/
DOMAIN=localhost
+17
View File
@@ -0,0 +1,17 @@
services:
forgejo:
image: codeberg.org/forgejo/forgejo:16
container_name: forgejo
restart: unless-stopped
environment:
- USER_UID=${PUID:-1000}
- USER_GID=${PGID:-1000}
- FORGEJO__server__ROOT_URL=${ROOT_URL:-http://localhost:3000/}
- FORGEJO__server__DOMAIN=${DOMAIN:-localhost}
ports:
- "${HTTP_PORT:-3301}:3000"
- "${SSH_PORT:-2223}:22"
volumes:
- ${DATA_PATH:-/srv/forgejo}:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Forgejo",
"description": "Community-run Git forge with CI (Forgejo Actions) — the Gitea fork.",
"tags": [
"git",
"dev"
],
"gpu": null
}
+4
View File
@@ -0,0 +1,4 @@
TZ=Europe/Berlin
DATA_PATH=/srv/freshrss
HTTP_PORT=8093
CRON_MIN=*/20
+13
View File
@@ -0,0 +1,13 @@
services:
freshrss:
image: freshrss/freshrss:latest
container_name: freshrss
restart: unless-stopped
environment:
- TZ=${TZ:-Europe/Berlin}
- CRON_MIN=${CRON_MIN:-*/20}
ports:
- "${HTTP_PORT:-8093}:80"
volumes:
- ${DATA_PATH:-/srv/freshrss}/data:/var/www/FreshRSS/data
- ${DATA_PATH:-/srv/freshrss}/extensions:/var/www/FreshRSS/extensions
+9
View File
@@ -0,0 +1,9 @@
{
"name": "FreshRSS",
"description": "Self-hosted RSS aggregator with an API for mobile readers, SQLite by default.",
"tags": [
"rss",
"productivity"
],
"gpu": null
}
+8
View File
@@ -0,0 +1,8 @@
TZ=Europe/Berlin
DATA_PATH=/srv/frigate
MEDIA_PATH=/srv/frigate/media
HTTP_PORT=8971
RTSP_PORT=8554
WEBRTC_PORT=8555
SHM_SIZE=256mb
RTSP_PASSWORD=change-me
+32
View File
@@ -0,0 +1,32 @@
services:
frigate:
image: ghcr.io/blakeblackshear/frigate:stable
container_name: frigate
restart: unless-stopped
privileged: true
shm_size: ${SHM_SIZE:-256mb}
environment:
- TZ=${TZ:-Europe/Berlin}
- FRIGATE_RTSP_PASSWORD=${RTSP_PASSWORD:-change-me}
ports:
- "${HTTP_PORT:-8971}:8971"
- "${RTSP_PORT:-8554}:8554"
- "${WEBRTC_PORT:-8555}:8555/tcp"
- "${WEBRTC_PORT:-8555}:8555/udp"
volumes:
# The config lives with the stack so you can edit it here; the directory
# underneath holds Frigate's database and model cache.
- ${DATA_PATH:-/srv/frigate}/config:/config
- ./config.yml:/config/config.yml
- ${MEDIA_PATH:-/srv/frigate/media}:/media/frigate
- /etc/localtime:/etc/localtime:ro
- type: tmpfs
target: /tmp/cache
tmpfs:
size: 1000000000
# Uncomment what your host actually has:
# devices:
# # Google Coral USB accelerator
# - /dev/bus/usb:/dev/bus/usb
# # Intel iGPU for hardware decoding
# - /dev/dri/renderD128:/dev/dri/renderD128
+38
View File
@@ -0,0 +1,38 @@
mqtt:
enabled: false
detectors:
cpu1:
type: cpu
# Swap for a Coral TPU once you have one:
# coral:
# type: edgetpu
# device: usb
cameras:
front_door:
ffmpeg:
inputs:
- path: rtsp://user:{FRIGATE_RTSP_PASSWORD}@192.168.1.50:554/stream1
roles:
- detect
- record
detect:
width: 1280
height: 720
fps: 5
objects:
track:
- person
- car
record:
enabled: true
retain:
days: 7
mode: motion
snapshots:
enabled: true
retain:
default: 14
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Frigate NVR",
"description": "Network video recorder with real-time object detection. Edit config.yml with your cameras before deploying.",
"tags": [
"home-automation",
"video",
"monitoring"
],
"gpu": "Coral TPU / Intel / NVIDIA optional"
}
+6
View File
@@ -0,0 +1,6 @@
TZ=Europe/Berlin
DATA_PATH=/srv/gotify
HTTP_PORT=8098
ADMIN_USER=admin
# Required.
ADMIN_PASSWORD=
+13
View File
@@ -0,0 +1,13 @@
services:
gotify:
image: gotify/server:latest
container_name: gotify
restart: unless-stopped
environment:
- TZ=${TZ:-Europe/Berlin}
- GOTIFY_DEFAULTUSER_NAME=${ADMIN_USER:-admin}
- GOTIFY_DEFAULTUSER_PASS=${ADMIN_PASSWORD:?admin password required}
ports:
- "${HTTP_PORT:-8098}:80"
volumes:
- ${DATA_PATH:-/srv/gotify}:/app/data
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Gotify",
"description": "Simple push notification server with per-application tokens and an Android app.",
"tags": [
"notifications",
"monitoring"
],
"gpu": null
}
+8
View File
@@ -0,0 +1,8 @@
PUID=1000
PGID=1000
DATA_PATH=/srv/grafana
HTTP_PORT=3030
ROOT_URL=http://localhost:3000
ADMIN_USER=admin
ADMIN_PASSWORD=change-me
INSTALL_PLUGINS=
+15
View File
@@ -0,0 +1,15 @@
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
user: "${PUID:-1000}:${PGID:-1000}"
environment:
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-change-me}
- GF_SERVER_ROOT_URL=${ROOT_URL:-http://localhost:3000}
- GF_INSTALL_PLUGINS=${INSTALL_PLUGINS:-}
ports:
- "${HTTP_PORT:-3030}:3000"
volumes:
- ${DATA_PATH:-/srv/grafana}:/var/lib/grafana
+10
View File
@@ -0,0 +1,10 @@
{
"name": "Grafana",
"description": "Dashboards and alerting on top of Prometheus, InfluxDB, Loki and friends.",
"tags": [
"monitoring",
"metrics",
"dashboard"
],
"gpu": null
}
+5
View File
@@ -0,0 +1,5 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/grocy
HTTP_PORT=9283
+13
View File
@@ -0,0 +1,13 @@
services:
grocy:
image: lscr.io/linuxserver/grocy:latest
container_name: grocy
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-9283}:80"
volumes:
- ${DATA_PATH:-/srv/grocy}:/config
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Grocy",
"description": "Groceries, stock, chores and household ERP with barcode scanning. First login: admin / admin.",
"tags": [
"productivity",
"recipes"
],
"gpu": null
}
+6
View File
@@ -0,0 +1,6 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
DATA_PATH=/srv/heimdall
HTTP_PORT=8102
HTTPS_PORT=8103
+14
View File
@@ -0,0 +1,14 @@
services:
heimdall:
image: lscr.io/linuxserver/heimdall:latest
container_name: heimdall
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-8102}:80"
- "${HTTPS_PORT:-8103}:443"
volumes:
- ${DATA_PATH:-/srv/heimdall}:/config
+8
View File
@@ -0,0 +1,8 @@
{
"name": "Heimdall",
"description": "Simple application dashboard with enhanced tiles for common services.",
"tags": [
"dashboard"
],
"gpu": null
}
+4
View File
@@ -0,0 +1,4 @@
DATA_PATH=/srv/homarr
HTTP_PORT=7575
# Required. Generate with: openssl rand -hex 32
SECRET_ENCRYPTION_KEY=
+14
View File
@@ -0,0 +1,14 @@
services:
homarr:
image: ghcr.io/homarr-labs/homarr:latest
container_name: homarr
restart: unless-stopped
environment:
# Required. Generate with: openssl rand -hex 32
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY:?encryption key required}
ports:
- "${HTTP_PORT:-7575}:7575"
volumes:
- ${DATA_PATH:-/srv/homarr}:/appdata
# Optional: lets Homarr show container status
- /var/run/docker.sock:/var/run/docker.sock:ro
+8
View File
@@ -0,0 +1,8 @@
{
"name": "Homarr",
"description": "Drag-and-drop dashboard with integrations for the *arr apps, Docker and more.",
"tags": [
"dashboard"
],
"gpu": null
}
@@ -0,0 +1,3 @@
TZ=Europe/Berlin
DATA_PATH=/srv/homeassistant
# Runs on host networking — the UI is on port 8123 of the host itself.
@@ -0,0 +1,17 @@
services:
homeassistant:
image: ghcr.io/home-assistant/home-assistant:stable
container_name: homeassistant
restart: unless-stopped
# Host networking lets HA discover devices via mDNS, SSDP and DHCP.
# Swap it for a ports mapping if you only use cloud/MQTT integrations.
network_mode: host
privileged: true
environment:
- TZ=${TZ:-Europe/Berlin}
volumes:
- ${DATA_PATH:-/srv/homeassistant}:/config
- /run/dbus:/run/dbus:ro
# Uncomment to pass through a Zigbee/Z-Wave USB stick:
# devices:
# - /dev/ttyUSB0:/dev/ttyUSB0
@@ -0,0 +1,9 @@
{
"name": "Home Assistant",
"description": "Home automation hub with thousands of integrations. Runs on host networking for device discovery.",
"tags": [
"home-automation",
"iot"
],
"gpu": null
}
+5
View File
@@ -0,0 +1,5 @@
PUID=1000
PGID=1000
DATA_PATH=/srv/homepage
HTTP_PORT=3005
ALLOWED_HOSTS=*
+15
View File
@@ -0,0 +1,15 @@
services:
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
# Comma-separated hostnames the UI may be reached under, or "*" to allow all.
- HOMEPAGE_ALLOWED_HOSTS=${ALLOWED_HOSTS:-*}
ports:
- "${HTTP_PORT:-3005}:3000"
volumes:
- ${DATA_PATH:-/srv/homepage}:/app/config
- /var/run/docker.sock:/var/run/docker.sock:ro
+8
View File
@@ -0,0 +1,8 @@
{
"name": "Homepage",
"description": "YAML-configured start page with live widgets for dozens of self-hosted services.",
"tags": [
"dashboard"
],
"gpu": null
}
+11
View File
@@ -0,0 +1,11 @@
TZ=Europe/Berlin
IMMICH_VERSION=release
DATA_PATH=/srv/immich
# Where uploaded photos and videos live. Network shares are fine here.
UPLOAD_LOCATION=/srv/immich/library
# The database must sit on local storage — never on NFS/SMB.
DB_DATA_LOCATION=/srv/immich/postgres
DB_USERNAME=postgres
DB_PASSWORD=change-me
DB_DATABASE_NAME=immich
HTTP_PORT=2283
+51
View File
@@ -0,0 +1,51 @@
services:
immich-server:
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
container_name: immich-server
restart: unless-stopped
depends_on:
- redis
- database
environment:
- TZ=${TZ:-Europe/Berlin}
- DB_HOSTNAME=database
- DB_USERNAME=${DB_USERNAME:-postgres}
- DB_PASSWORD=${DB_PASSWORD:-postgres}
- DB_DATABASE_NAME=${DB_DATABASE_NAME:-immich}
- REDIS_HOSTNAME=redis
ports:
- "${HTTP_PORT:-2283}:2283"
volumes:
- ${UPLOAD_LOCATION:-/srv/immich/library}:/data
- /etc/localtime:/etc/localtime:ro
# Uncomment for Intel/AMD hardware transcoding:
# devices:
# - /dev/dri:/dev/dri
immich-machine-learning:
# Add -cuda / -openvino / -rocm to the tag for accelerated inference.
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
container_name: immich-machine-learning
restart: unless-stopped
volumes:
- ${DATA_PATH:-/srv/immich}/model-cache:/cache
redis:
image: valkey/valkey:8-alpine
container_name: immich-redis
restart: unless-stopped
healthcheck:
test: redis-cli ping | grep -q PONG || exit 1
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
container_name: immich-postgres
restart: unless-stopped
shm_size: 128mb
environment:
- POSTGRES_USER=${DB_USERNAME:-postgres}
- POSTGRES_PASSWORD=${DB_PASSWORD:-postgres}
- POSTGRES_DB=${DB_DATABASE_NAME:-immich}
- POSTGRES_INITDB_ARGS=--data-checksums
volumes:
- ${DB_DATA_LOCATION:-/srv/immich/postgres}:/var/lib/postgresql/data

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