Compare commits
98 Commits
0c901180d9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 274f947279 | |||
| 4e9fa05dd4 | |||
| f33aabd9ae | |||
| 0a4c4b4d03 | |||
| 4600b89d3e | |||
| 94f09bd3ce | |||
| 144f5678f7 | |||
| 6b3719db95 | |||
| b6288e6fc9 | |||
| fc01e3b5c7 | |||
| 021cef6e25 | |||
| 27b5d20b49 | |||
| 3fea6e5432 | |||
| b169c0beb7 | |||
| 9faa1b4f6d | |||
| 9ce0bfaf70 | |||
| f3dddbd21f | |||
| 14aad65e0f | |||
| 6516f8472d | |||
| 1a31ff6708 | |||
| 2a90e09607 | |||
| 6ad6c47883 | |||
| 02e12d6a63 | |||
| 44c8bf74a2 | |||
| e13167f098 | |||
| 531fb2cdc1 | |||
| de1df5375b | |||
| 0ce7315e11 | |||
| 1fa30ad70c | |||
| 888aa91da8 | |||
| 9be0f377ad | |||
| 991bd9be31 | |||
| a874027a00 | |||
| 5d2214b612 | |||
| 4f80f77313 | |||
| f492d7f41a | |||
| e16e838a12 | |||
| 523a2ecb13 | |||
| 2805aeaea3 | |||
| fb5b281a2b | |||
| 98517b8dc0 | |||
| 6e6ef1a477 | |||
| c74b60a329 | |||
| f64c449460 | |||
| ed7b96a024 | |||
| 4ef86b5c1c | |||
| 53d963d2c4 | |||
| 94fd57e1e6 | |||
| c7bb480826 | |||
| 74ea269c3d | |||
| 4b350f47f2 | |||
| 2e159c285b | |||
| a6da94951d | |||
| 610b00531b | |||
| 3b7e2974fe | |||
| c4253a2efd | |||
| 160e0781b7 | |||
| d1b6c17bac | |||
| 694b77bea5 | |||
| 6a09720f6e | |||
| 487375901f | |||
| dcfd7f1c7f | |||
| 9ed11af52a | |||
| 3f132a9169 | |||
| d0def2e259 | |||
| 51b2edf92b | |||
| 0abcc6dcf4 | |||
| 1716238835 | |||
| a33d883580 | |||
| fc51e15cac | |||
| 2867b43a9c | |||
| e74211030e | |||
| 667b01e62f | |||
| cc615cc885 | |||
|
69ac3adea8
|
|||
|
ed79177458
|
|||
|
436ac40977
|
|||
|
24978964a1
|
|||
|
6a7b88543e
|
|||
|
74054f33fd
|
|||
|
1431f2c1ca
|
|||
| 5e2c1eec63 | |||
| ad8e8d6cd7 | |||
| ac812e47d2 | |||
|
fc569046b7
|
|||
|
43fb33511e
|
|||
|
31c8fc8b42
|
|||
|
096ffd851b
|
|||
|
4d643bea1a
|
|||
|
8d7b353cc2
|
|||
|
ad6bff89cb
|
|||
|
72b13f80fb
|
|||
|
773149e931
|
|||
|
adbd765e72
|
|||
|
d075f470cd
|
|||
|
ece646d07a
|
|||
|
5503c2c6f4
|
|||
|
82ed5a3fb0
|
30
3d_printing/spoolman/docker-compose.yml
Normal file
30
3d_printing/spoolman/docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
services:
|
||||||
|
spoolman:
|
||||||
|
container_name: spoolman
|
||||||
|
image: ghcr.io/donkie/spoolman:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/spoolman:/home/app/.local/share/spoolman
|
||||||
|
labels:
|
||||||
|
homepage.group: "3D Printing"
|
||||||
|
homepage.name: "Spoolman"
|
||||||
|
homepage.icon: "spoolman.png"
|
||||||
|
homepage.href: "https://spoolman.${DOMAIN}"
|
||||||
|
homepage.description: "Filament Inventory Manager"
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.spoolman.rule: "Host(`spoolman.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.spoolman.entrypoints: "https"
|
||||||
|
traefik.http.routers.spoolman.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.spoolman.service: "spoolman"
|
||||||
|
traefik.http.routers.spoolman.middlewares: "voidauth@docker"
|
||||||
|
traefik.http.services.spoolman.loadbalancer.server.port: "8000"
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/London
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
75
AGENTS.md
Normal file
75
AGENTS.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Repository Architecture & Deployment Guide
|
||||||
|
|
||||||
|
This document outlines the structure and deployment strategy for the Kendrick Homelab repository. It is intended to guide AI agents and developers in understanding the system's organization.
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
This repository serves as the "Infrastructure as Code" (IaC) source for a self-hosted homelab. It is designed to be consumed by **Portainer** using its Git integration feature (Stacks).
|
||||||
|
|
||||||
|
- **Primary Goal:** specific, modular deployment of Docker services.
|
||||||
|
- **Deployment Method:** Portainer Stacks (Git Repository).
|
||||||
|
- **Orchestration:** Docker Compose.
|
||||||
|
|
||||||
|
## 2. Directory Structure
|
||||||
|
|
||||||
|
The repository follows a strict **Category > Service > Configuration** hierarchy. This ensures that every service is self-contained and easily locatable.
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── category_name/ # e.g., "access_management", "media"
|
||||||
|
│ └── service_name/ # e.g., "authentik", "jellyfin"
|
||||||
|
│ ├── docker-compose.yml # The service definition
|
||||||
|
│ └── .env.example # (Optional) Template for environment variables
|
||||||
|
├── backups/ # Backup configurations
|
||||||
|
├── dashboards/ # Dashboard services (e.g., Homepage)
|
||||||
|
└── monitoring/ # System monitoring stack
|
||||||
|
```
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- **Categories:** Lowercase, snake_case (e.g., `access_management`).
|
||||||
|
- **Services:** Lowercase, hyphen-separated if needed (e.g., `core-keeper`).
|
||||||
|
- **Files:** Strictly `docker-compose.yml` for Portainer compatibility.
|
||||||
|
|
||||||
|
## 3. Deployment Strategy (Portainer)
|
||||||
|
|
||||||
|
Each subdirectory containing a `docker-compose.yml` is intended to be deployed as a separate **Stack** in Portainer.
|
||||||
|
|
||||||
|
- **Repository URL:** This git repository.
|
||||||
|
- **Compose Path:** Relative path to the file (e.g., `proxies/traefik/docker-compose.yml`).
|
||||||
|
- **Environment Variables:** injected via the Portainer UI ("Environment variables" section) for each stack. Do not hardcode secrets in Git.
|
||||||
|
- **Security:** Never include actual secrets (API keys, passwords) in `docker-compose.yml`. Use environment variable placeholders (e.g., `${API_KEY}`).
|
||||||
|
|
||||||
|
## 4. Common Patterns & Configuration
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
- **External Network:** Most public-facing services connect to a pre-defined external network (typically `traefik_public` or similar) to allow the reverse proxy to route traffic to them.
|
||||||
|
- **Internal Communication:** Services within the same stack communicate via the default bridge network created by Docker Compose.
|
||||||
|
|
||||||
|
### Reverse Proxy (Traefik/NPM)
|
||||||
|
- **Traefik:** Used as the primary ingress controller. Services use labels (e.g., `traefik.enable=true`) to expose themselves.
|
||||||
|
- **Nginx Proxy Manager (NPM):** Available as an alternative or secondary proxy in `proxies/npm`.
|
||||||
|
|
||||||
|
### Dashboard Integration (Homepage)
|
||||||
|
- **Discovery:** Services utilize specific Docker labels (e.g., `homepage.group`, `homepage.name`) to automatically register themselves on the central dashboard.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
Common variables expected across stacks:
|
||||||
|
- `DOMAIN`: The root domain name (e.g., `example.com`).
|
||||||
|
- `CONFIG_ROOT`: Path on the host system where persistent configuration is stored.
|
||||||
|
- `PUID`/`PGID`: User and Group IDs for file permission management.
|
||||||
|
|
||||||
|
## 5. Service Inventory
|
||||||
|
|
||||||
|
| Category | Services Included |
|
||||||
|
|----------------------|-------------------|
|
||||||
|
| **Access Management**| Authentik |
|
||||||
|
| **Backups** | Zerobyte |
|
||||||
|
| **Container Mgmt** | Portainer |
|
||||||
|
| **Dashboards** | Homepage |
|
||||||
|
| **DNS** | AdGuard Home, DuckDNS |
|
||||||
|
| **Games** | Core Keeper |
|
||||||
|
| **Media** | ArrStack (Radarr/Sonarr etc.), Jellyfin, Plex |
|
||||||
|
| **Monitoring** | Beszel, Glances, Uptime Kuma |
|
||||||
|
| **Proxies** | Nginx Proxy Manager (NPM), Traefik |
|
||||||
|
| **Remote Access** | Cloudflared |
|
||||||
|
| **Version Control** | Gitea |
|
||||||
10
README.md
10
README.md
@@ -53,13 +53,21 @@ Tools to keep the ship sailing smooth.
|
|||||||
```
|
```
|
||||||
Because all work and no play makes the server a dull boy.
|
Because all work and no play makes the server a dull boy.
|
||||||
|
|
||||||
|
* **Books**: `books/booklore` - eBook management (Booklore).
|
||||||
* **Games**: `games/core-keeper` - Dedicated server for Core Keeper.
|
* **Games**: `games/core-keeper` - Dedicated server for Core Keeper.
|
||||||
* **Media**: `media/` - (Coming Soon) The media stack.
|
* **Media - Jellyfin**: `media/jellyfin` - Jellyfin Media Server.
|
||||||
|
* **Media - Plex**: `media/plex` - Plex Media Server.
|
||||||
|
* **Media - ArrStack**: `media/arrstack` - The *Arr stack.
|
||||||
|
* **3D Printing**: `3d_printing/spoolman` - Filament inventory manager (Spoolman).
|
||||||
|
|
||||||
### Directory Structure
|
### Directory Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
|
├── 3d_printing/
|
||||||
|
│ └── spoolman/
|
||||||
|
├── books/
|
||||||
|
│ └── booklore/
|
||||||
├── container_management/
|
├── container_management/
|
||||||
│ └── portainer/
|
│ └── portainer/
|
||||||
├── dns/
|
├── dns/
|
||||||
|
|||||||
@@ -60,12 +60,17 @@ services:
|
|||||||
traefik.http.routers.authentik.rule: "Host(`auth.${DOMAIN}`)"
|
traefik.http.routers.authentik.rule: "Host(`auth.${DOMAIN}`)"
|
||||||
traefik.http.routers.authentik.entrypoints: "https"
|
traefik.http.routers.authentik.entrypoints: "https"
|
||||||
traefik.http.routers.authentik.tls.certresolver: "cloudflare"
|
traefik.http.routers.authentik.tls.certresolver: "cloudflare"
|
||||||
traefik.http.services.authentik.loadbalancer.server.port: "9000"
|
# Authentik Outpost Router (prevents redirect loops)
|
||||||
# Authentik Outpost (Handling auth callbacks for all domains)
|
traefik.http.routers.authentik-outpost.rule: "PathPrefix(`/outpost.goauthentik.io/`)"
|
||||||
traefik.http.routers.authentik-outpost.rule: "HostRegexp(`{host:.+}`) && PathPrefix(`/outpost.goauthentik.io/`)"
|
traefik.http.routers.authentik-outpost.priority: "100"
|
||||||
traefik.http.routers.authentik-outpost.entrypoints: "https"
|
traefik.http.routers.authentik-outpost.entrypoints: "https"
|
||||||
traefik.http.routers.authentik-outpost.tls.certresolver: "cloudflare"
|
traefik.http.routers.authentik-outpost.tls.certresolver: "cloudflare"
|
||||||
traefik.http.routers.authentik-outpost.service: "authentik"
|
traefik.http.routers.authentik-outpost.service: "authentik"
|
||||||
|
traefik.http.services.authentik.loadbalancer.server.port: "9000"
|
||||||
|
# Authentik Middleware
|
||||||
|
traefik.http.middlewares.authentik.forwardauth.address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
||||||
|
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: "true"
|
||||||
|
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: "X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version"
|
||||||
# Homepage
|
# Homepage
|
||||||
homepage.group: "Management"
|
homepage.group: "Management"
|
||||||
homepage.name: "Authentik"
|
homepage.name: "Authentik"
|
||||||
|
|||||||
45
access_management/void/docker-compose.yml
Normal file
45
access_management/void/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
services:
|
||||||
|
voidauth:
|
||||||
|
image: voidauth/voidauth:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/voidauth/config:/app/config
|
||||||
|
depends_on:
|
||||||
|
voidauth-db:
|
||||||
|
condition: service_healthy
|
||||||
|
labels:
|
||||||
|
traefik.enable: 'true'
|
||||||
|
traefik.http.routers.voidauth.rule: "Host(`auth.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.voidauth.entryPoints: 'https'
|
||||||
|
traefik.http.routers.voidauth.tls: 'true'
|
||||||
|
traefik.http.routers.voidauth.service: "voidauth"
|
||||||
|
traefik.http.services.voidauth.loadbalancer.server.port: "3000"
|
||||||
|
traefik.http.middlewares.voidauth.forwardAuth.address: 'http://voidauth:3000/api/authz/forward-auth'
|
||||||
|
traefik.http.middlewares.voidauth.forwardAuth.trustForwardHeader: 'true'
|
||||||
|
traefik.http.middlewares.voidauth.forwardAuth.authResponseHeaders: 'Remote-User,Remote-Name,Remote-Email,Remote-Groups'
|
||||||
|
traefik.docker.network: "traefik_public"
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik_public
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
voidauth-db:
|
||||||
|
image: postgres:18
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: # required, same as voidauth DB_PASSWORD
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/voidauth/db:/var/lib/postgresql/18/docker
|
||||||
|
healthcheck:
|
||||||
|
test: "pg_isready -U postgres -h localhost"
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
4
access_management/void/example.env
Normal file
4
access_management/void/example.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
APP_URL: # required, ex. https://auth.example.com
|
||||||
|
STORAGE_KEY: # required
|
||||||
|
DB_PASSWORD: # required, same as voidauth-db POSTGRES_PASSWORD
|
||||||
|
DB_HOST: voidauth-db # required
|
||||||
4
bookmarks/karakeep/.env.example
Normal file
4
bookmarks/karakeep/.env.example
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
KARAKEEP_VERSION=latest
|
||||||
|
NEXTAUTH_SECRET=changeme
|
||||||
|
MEILI_MASTER_KEY=changeme
|
||||||
|
GEMINI_API_KEY=changeme
|
||||||
76
bookmarks/karakeep/docker-compose.yml
Normal file
76
bookmarks/karakeep/docker-compose.yml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
karakeep:
|
||||||
|
image: ghcr.io/karakeep-app/karakeep:latest
|
||||||
|
container_name: karakeep
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- meilisearch
|
||||||
|
- chrome
|
||||||
|
environment:
|
||||||
|
- MEILI_ADDR=http://meilisearch:7700
|
||||||
|
- BROWSER_WEB_URL=http://chrome:9222
|
||||||
|
- DATA_DIR=/data
|
||||||
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||||
|
- NEXTAUTH_URL=https://karakeep.${DOMAIN}
|
||||||
|
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
|
||||||
|
- OPENAI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
|
||||||
|
- OPENAI_API_KEY=${GEMINI_API_KEY}
|
||||||
|
- INFERENCE_TEXT_MODEL=gemini-2.5-flash
|
||||||
|
- INFERENCE_IMAGE_MODEL=gemini-2.5-flash
|
||||||
|
- OAUTH_CLIENT_ID=${OAUTH_CLIENT_ID}
|
||||||
|
- OAUTH_CLIENT_SECRET=${OAUTH_CLIENT_SECRET}
|
||||||
|
- OAUTH_WELLKNOWN_URL=https://auth.${DOMAIN}/application/o/karakeep/.well-known/openid-configuration
|
||||||
|
- OAUTH_PROVIDER_NAME=authentik
|
||||||
|
- OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING=true
|
||||||
|
- DISABLE_PASSWORD_AUTH=true
|
||||||
|
- DISABLE_SIGNUPS=true
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/karakeep/data:/data
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.docker.network=traefik_public"
|
||||||
|
- "traefik.http.routers.karakeep.rule=Host(`karakeep.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.karakeep.entrypoints=https"
|
||||||
|
- "traefik.http.routers.karakeep.tls.certresolver=cloudflare"
|
||||||
|
- "traefik.http.services.karakeep.loadbalancer.server.port=3000"
|
||||||
|
- "homepage.group=Bookmarks"
|
||||||
|
- "homepage.name=Karakeep"
|
||||||
|
- "homepage.icon=karakeep.png"
|
||||||
|
- "homepage.href=https://karakeep.${DOMAIN}"
|
||||||
|
- "homepage.description=AI Bookmarking Tool"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
- internal
|
||||||
|
|
||||||
|
meilisearch:
|
||||||
|
image: getmeili/meilisearch:v1.13.3
|
||||||
|
container_name: karakeep-meilisearch
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
|
||||||
|
- MEILI_NO_ANALYTICS=true
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/karakeep/meili_data:/meili_data
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
chrome:
|
||||||
|
image: gcr.io/zenika-hub/alpine-chrome:124
|
||||||
|
restart: unless-stopped
|
||||||
|
command:
|
||||||
|
- --no-sandbox
|
||||||
|
- --disable-gpu
|
||||||
|
- --disable-dev-shm-usage
|
||||||
|
- --remote-debugging-address=0.0.0.0
|
||||||
|
- --remote-debugging-port=9222
|
||||||
|
- --hide-scrollbars
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
||||||
3
books/booklore/.env.example
Normal file
3
books/booklore/.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
BOOKLORE_DB_PASSWORD=secret
|
||||||
|
BOOKLORE_DB_ROOT_PASSWORD=secret
|
||||||
|
BOOKLORE_DB_USER=booklore
|
||||||
72
books/booklore/docker-compose.yml
Normal file
72
books/booklore/docker-compose.yml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
services:
|
||||||
|
booklore:
|
||||||
|
image: booklore/booklore:latest
|
||||||
|
# Alternative: Use GitHub Container Registry
|
||||||
|
# image: ghcr.io/booklore-app/booklore:latest
|
||||||
|
container_name: booklore
|
||||||
|
environment:
|
||||||
|
- USER_ID=${APP_USER_ID}
|
||||||
|
- GROUP_ID=${APP_GROUP_ID}
|
||||||
|
- TZ=${TZ}
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- DATABASE_USERNAME=${DB_USER}
|
||||||
|
- DATABASE_PASSWORD=${DB_PASSWORD}
|
||||||
|
- BOOKLORE_PORT=${BOOKLORE_PORT}
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
expose:
|
||||||
|
- "${BOOKLORE_PORT}"
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.docker.network: "traefik_public"
|
||||||
|
traefik.http.routers.booklore.rule: "Host(`booklore.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.booklore.entrypoints: "https"
|
||||||
|
traefik.http.routers.booklore.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.booklore.service: "booklore"
|
||||||
|
traefik.http.services.booklore.loadbalancer.server.port: "${BOOKLORE_PORT}"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Books"
|
||||||
|
homepage.name: "BookLore"
|
||||||
|
homepage.icon: "booklore.png"
|
||||||
|
homepage.href: "https://booklore.${DOMAIN}"
|
||||||
|
homepage.description: "Book Manager"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
- default
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/booklore/data:/app/data
|
||||||
|
- ${MEDIA_PATH}/books:/books
|
||||||
|
- ${MEDIA_PATH}/bookdrop:/bookdrop
|
||||||
|
healthcheck:
|
||||||
|
test: wget -q -O - http://localhost:${BOOKLORE_PORT}/api/v1/healthcheck
|
||||||
|
interval: 60s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
timeout: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: lscr.io/linuxserver/mariadb:11.4.5
|
||||||
|
container_name: mariadb
|
||||||
|
environment:
|
||||||
|
- PUID=${DB_USER_ID}
|
||||||
|
- PGID=${DB_GROUP_ID}
|
||||||
|
- TZ=${TZ}
|
||||||
|
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||||
|
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||||
|
- MYSQL_USER=${DB_USER}
|
||||||
|
- MYSQL_PASSWORD=${DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./mariadb/config:/config
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "mariadb-admin", "ping", "-h", "localhost" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
88
container_management/komodo/mongo.compose.yaml
Normal file
88
container_management/komodo/mongo.compose.yaml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
################################
|
||||||
|
# 🦎 KOMODO COMPOSE - MONGO 🦎 #
|
||||||
|
################################
|
||||||
|
|
||||||
|
## This compose file will deploy:
|
||||||
|
## 1. MongoDB
|
||||||
|
## 2. Komodo Core
|
||||||
|
## 3. Komodo Periphery
|
||||||
|
|
||||||
|
services:
|
||||||
|
mongo:
|
||||||
|
image: mongo
|
||||||
|
labels:
|
||||||
|
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
|
||||||
|
command: --quiet --wiredTigerCacheSizeGB 0.25
|
||||||
|
restart: unless-stopped
|
||||||
|
# ports:
|
||||||
|
# - 27017:27017
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/komodo/mongo-data:/data/db
|
||||||
|
- ${CONFIG_ROOT}/komodo/mongo-config:/data/configdb
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: ${KOMODO_DB_USERNAME}
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: ${KOMODO_DB_PASSWORD}
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
core:
|
||||||
|
image: ghcr.io/moghtech/komodo-core:${COMPOSE_KOMODO_IMAGE_TAG:-latest}
|
||||||
|
labels:
|
||||||
|
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.komodo.rule: "Host(`containers.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.komodo.entrypoints: "https"
|
||||||
|
traefik.http.routers.komodo.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.komodo.service: "komodo"
|
||||||
|
traefik.http.services.komodo.loadbalancer.server.port: "9120"
|
||||||
|
traefik.docker.network: "traefik_public"
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
env_file: ./compose.env
|
||||||
|
environment:
|
||||||
|
KOMODO_DATABASE_ADDRESS: mongo:27017
|
||||||
|
KOMODO_DATABASE_USERNAME: ${KOMODO_DB_USERNAME}
|
||||||
|
KOMODO_DATABASE_PASSWORD: ${KOMODO_DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
## Store dated backups of the database - https://komo.do/docs/setup/backup
|
||||||
|
- ${COMPOSE_KOMODO_BACKUPS_PATH}:/backups
|
||||||
|
## Store sync files on server
|
||||||
|
- ${CONFIG_ROOT}/komodo/syncs:/syncs
|
||||||
|
## Optionally mount a custom core.config.toml
|
||||||
|
# - /path/to/core.config.toml:/config/config.toml
|
||||||
|
## Allows for systemd Periphery connection at
|
||||||
|
## "https://host.docker.internal:8120"
|
||||||
|
# extra_hosts:
|
||||||
|
# - host.docker.internal:host-gateway
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
## 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
|
||||||
|
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}
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
|
internal:
|
||||||
|
driver: bridge
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
services:
|
|
||||||
portainer:
|
|
||||||
container_name: portainer
|
|
||||||
image: portainer/portainer-ce:lts
|
|
||||||
restart: always
|
|
||||||
labels:
|
|
||||||
homepage.group: "Management"
|
|
||||||
homepage.name: "Portainer"
|
|
||||||
homepage.icon: "portainer.png"
|
|
||||||
homepage.href: "https://portainer.${DOMAIN}"
|
|
||||||
homepage.description: "Container Management"
|
|
||||||
traefik.enable: "true"
|
|
||||||
traefik.http.routers.portainer.rule: "Host(`portainer.${DOMAIN}`)"
|
|
||||||
traefik.http.routers.portainer.entrypoints: "https"
|
|
||||||
traefik.http.routers.portainer.tls.certresolver: "cloudflare"
|
|
||||||
traefik.http.routers.portainer.service: "portainer"
|
|
||||||
traefik.http.services.portainer.loadbalancer.server.port: "9443"
|
|
||||||
traefik.http.services.portainer.loadbalancer.server.scheme: "https"
|
|
||||||
traefik.http.services.portainer.loadbalancer.serverstransport: "insecure@file"
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- portainer_data:/data
|
|
||||||
ports:
|
|
||||||
- 9443:9443
|
|
||||||
networks:
|
|
||||||
- traefik_public
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
portainer_data:
|
|
||||||
name: portainer_data
|
|
||||||
|
|
||||||
networks:
|
|
||||||
traefik_public:
|
|
||||||
external: true
|
|
||||||
24
dashboards/glance/docker-compose.yml
Normal file
24
dashboards/glance/docker-compose.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
glance:
|
||||||
|
container_name: glance
|
||||||
|
image: glanceapp/glance
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/glance/config:/app/config
|
||||||
|
- ${CONFIG_ROOT}/glance/assets:/app/assets
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
env_file: .env
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
labels:
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.glance.rule: "Host(`glance.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.glance.entrypoints: "https"
|
||||||
|
traefik.http.routers.glance.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.glance.service: "glance"
|
||||||
|
traefik.http.services.glance.loadbalancer.server.port: "8080"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
services:
|
|
||||||
homepage:
|
|
||||||
image: ghcr.io/gethomepage/homepage:latest
|
|
||||||
container_name: homepage
|
|
||||||
expose:
|
|
||||||
- 3000
|
|
||||||
volumes:
|
|
||||||
- ${CONFIG_ROOT}/homepage/config:/app/config
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
environment:
|
|
||||||
HOMEPAGE_ALLOWED_HOSTS: "*"
|
|
||||||
restart: unless-stopped
|
|
||||||
labels:
|
|
||||||
homepage.group: "Dashboards"
|
|
||||||
homepage.name: "Homepage"
|
|
||||||
homepage.icon: "homepage.png"
|
|
||||||
homepage.href: "https://${DOMAIN}"
|
|
||||||
homepage.description: "The dashboard itself"
|
|
||||||
traefik.enable: "true"
|
|
||||||
traefik.http.routers.homepage.rule: "Host(`${DOMAIN}`)"
|
|
||||||
traefik.http.routers.homepage.entrypoints: "https"
|
|
||||||
traefik.http.routers.homepage.tls.certresolver: "cloudflare"
|
|
||||||
traefik.http.routers.homepage.service: "homepage"
|
|
||||||
traefik.http.services.homepage.loadbalancer.server.port: "3000"
|
|
||||||
networks:
|
|
||||||
- traefik_public
|
|
||||||
|
|
||||||
networks:
|
|
||||||
traefik_public:
|
|
||||||
external: true
|
|
||||||
@@ -13,7 +13,8 @@ services:
|
|||||||
traefik.http.routers.adguard.rule: "Host(`adguard.${DOMAIN}`)"
|
traefik.http.routers.adguard.rule: "Host(`adguard.${DOMAIN}`)"
|
||||||
traefik.http.routers.adguard.entrypoints: "https"
|
traefik.http.routers.adguard.entrypoints: "https"
|
||||||
traefik.http.routers.adguard.tls.certresolver: "cloudflare"
|
traefik.http.routers.adguard.tls.certresolver: "cloudflare"
|
||||||
traefik.http.services.adguard.loadbalancer.server.url: "http://${HOST_IP}:6969"
|
traefik.http.routers.adguard.middlewares: "voidauth@docker"
|
||||||
|
traefik.http.services.adguard.loadbalancer.server.address: "http://${HOST_IP}:6969"
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- ${CONFIG_ROOT}/adguard/work:/opt/adguardhome/work
|
- ${CONFIG_ROOT}/adguard/work:/opt/adguardhome/work
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
services:
|
|
||||||
duckdns:
|
|
||||||
container_name: duckdns
|
|
||||||
image: lscr.io/linuxserver/duckdns:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- SUBDOMAINS=${DUCKDNS_SUBDOMAINS}
|
|
||||||
- TOKEN=${DUCKDNS_TOKEN}
|
|
||||||
- TZ=${TZ}
|
|
||||||
volumes:
|
|
||||||
- ${CONFIG_ROOT}/duckdns/config:/config
|
|
||||||
labels:
|
|
||||||
homepage.group: "DNS"
|
|
||||||
homepage.name: "DuckDNS"
|
|
||||||
homepage.icon: "duckdns.png"
|
|
||||||
homepage.href: "https://www.duckdns.org"
|
|
||||||
homepage.description: "Dynamic DNS Updater"
|
|
||||||
networks:
|
|
||||||
- traefik_public
|
|
||||||
|
|
||||||
networks:
|
|
||||||
traefik_public:
|
|
||||||
external: true
|
|
||||||
68
documents/paperless/docker-compose.yml
Normal file
68
documents/paperless/docker-compose.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
broker:
|
||||||
|
image: docker.io/library/redis:7
|
||||||
|
container_name: paperless_redis
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/paperless/redis:/data
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: docker.io/library/postgres:16
|
||||||
|
container_name: paperless_db
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/paperless/pgdata:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: paperless
|
||||||
|
POSTGRES_USER: paperless
|
||||||
|
POSTGRES_PASSWORD: ${PAPERLESS_DB_PASS:-paperless}
|
||||||
|
|
||||||
|
webserver:
|
||||||
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
|
container_name: paperless
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
- broker
|
||||||
|
environment:
|
||||||
|
PAPERLESS_REDIS: redis://broker:6379
|
||||||
|
PAPERLESS_DBHOST: db
|
||||||
|
PAPERLESS_DBPORT: 5432
|
||||||
|
PAPERLESS_DBNAME: paperless
|
||||||
|
PAPERLESS_DBUSER: paperless
|
||||||
|
PAPERLESS_DBPASS: ${PAPERLESS_DB_PASS:-paperless}
|
||||||
|
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
|
||||||
|
PAPERLESS_URL: https://paperless.${DOMAIN}
|
||||||
|
PAPERLESS_TIME_ZONE: ${TZ:-Etc/UTC}
|
||||||
|
PAPERLESS_OCR_ROTATE_PAGES: clean
|
||||||
|
USERMAP_UID: ${PUID:-1000}
|
||||||
|
USERMAP_GID: ${PGID:-1000}
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/paperless/data:/usr/src/paperless/data
|
||||||
|
- ${CONFIG_ROOT}/paperless/media:/usr/src/paperless/media
|
||||||
|
- ${CONFIG_ROOT}/paperless/export:/usr/src/paperless/export
|
||||||
|
- ${CONFIG_ROOT}/paperless/consume:/usr/src/paperless/consume
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.docker.network: "traefik_public"
|
||||||
|
traefik.http.routers.paperless.rule: "Host(`paperless.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.paperless.entrypoints: "https"
|
||||||
|
traefik.http.routers.paperless.service: "paperless"
|
||||||
|
traefik.http.routers.paperless.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.services.paperless.loadbalancer.server.port: "8000"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Documents"
|
||||||
|
homepage.name: "Paperless-ngx"
|
||||||
|
homepage.icon: "paperless-ngx.svg"
|
||||||
|
homepage.href: "https://paperless.${DOMAIN}"
|
||||||
|
homepage.description: "Document Management"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
- default
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
37
food/mealie/docker-compose.yml
Normal file
37
food/mealie/docker-compose.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mealie:
|
||||||
|
image: ghcr.io/mealie-recipes/mealie:latest
|
||||||
|
container_name: mealie
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- BASE_URL=https://mealie.${DOMAIN}
|
||||||
|
- ALLOW_SIGNUP=true
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/mealie/data:/app/data
|
||||||
|
expose:
|
||||||
|
- 9000
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.mealie.rule: "Host(`mealie.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.mealie.entrypoints: "https"
|
||||||
|
traefik.http.routers.mealie.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.mealie.service: "mealie"
|
||||||
|
traefik.http.services.mealie.loadbalancer.server.port: "9000"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Food"
|
||||||
|
homepage.name: "Mealie"
|
||||||
|
homepage.icon: "mealie.png"
|
||||||
|
homepage.href: "https://mealie.${DOMAIN}"
|
||||||
|
homepage.description: "Recipe Manager"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
52
habit_tracking/habitica/docker-compose.yml
Normal file
52
habit_tracking/habitica/docker-compose.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: docker.io/awinterstein/habitica-server:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
environment:
|
||||||
|
- NODE_DB_URI=mongodb://mongo/habitica
|
||||||
|
- BASE_URL
|
||||||
|
- INVITE_ONLY # change to `true` after registration of initial users, to restrict further registrations
|
||||||
|
- EMAIL_SERVER_URL
|
||||||
|
- EMAIL_SERVER_PORT
|
||||||
|
- EMAIL_SERVER_AUTH_USER
|
||||||
|
- EMAIL_SERVER_AUTH_PASSWORD
|
||||||
|
- ADMIN_EMAIL
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
- habitica
|
||||||
|
labels:
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.docker.network: "traefik_public"
|
||||||
|
traefik.http.routers.habitica.rule: "Host(`habitica.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.habitica.entrypoints: "https"
|
||||||
|
traefik.http.routers.habitica.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.habitica.service: "habitica"
|
||||||
|
traefik.http.services.habitica.loadbalancer.server.port: "3000"
|
||||||
|
|
||||||
|
mongo:
|
||||||
|
image: docker.io/mongo:latest # better to replace 'latest' with the concrete mongo version (e.g., the most recent one)
|
||||||
|
restart: unless-stopped
|
||||||
|
hostname: mongo
|
||||||
|
command: ["--replSet", "rs", "--bind_ip_all", "--port", "27017"]
|
||||||
|
healthcheck:
|
||||||
|
test: echo "try { rs.status() } catch (err) { rs.initiate() }" | mongosh --port 27017 --quiet
|
||||||
|
interval: 10s
|
||||||
|
timeout: 30s
|
||||||
|
start_period: 0s
|
||||||
|
start_interval: 1s
|
||||||
|
retries: 30
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/habitica/db:/data/db:rw
|
||||||
|
- ${CONFIG_ROOT}/habitica/dbconf:/data/configdb
|
||||||
|
networks:
|
||||||
|
habitica:
|
||||||
|
aliases:
|
||||||
|
- mongo
|
||||||
|
networks:
|
||||||
|
habitica:
|
||||||
|
driver: bridge
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
@@ -9,10 +9,13 @@ services:
|
|||||||
devices:
|
devices:
|
||||||
- /dev/net/tun:/dev/net/tun
|
- /dev/net/tun:/dev/net/tun
|
||||||
environment:
|
environment:
|
||||||
|
- FIREWALL_OUTBOUND_SUBNETS=172.29.0.0/16,192.168.0.0/16
|
||||||
- VPN_SERVICE_PROVIDER=protonvpn
|
- VPN_SERVICE_PROVIDER=protonvpn
|
||||||
- VPN_TYPE=wireguard
|
- VPN_TYPE=wireguard
|
||||||
- WIREGUARD_PRIVATE_KEY=${PROTONVPN_WIREGUARD_PRIVATE_KEY}
|
- WIREGUARD_PRIVATE_KEY=${PROTONVPN_WIREGUARD_PRIVATE_KEY}
|
||||||
- SERVER_COUNTRIES=Netherlands
|
- SERVER_COUNTRIES=Denmark
|
||||||
|
# - OPENVPN_USER=${PROTON_OPENVPN_USER}
|
||||||
|
# - OPENVPN_PASS=${PROTON_OPENVPN_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- ${CONFIG_ROOT}/gluetun:/gluetun
|
- ${CONFIG_ROOT}/gluetun:/gluetun
|
||||||
ports:
|
ports:
|
||||||
@@ -43,6 +46,7 @@ services:
|
|||||||
traefik.http.routers.radarr.entrypoints: "https"
|
traefik.http.routers.radarr.entrypoints: "https"
|
||||||
traefik.http.routers.radarr.service: "radarr"
|
traefik.http.routers.radarr.service: "radarr"
|
||||||
traefik.http.routers.radarr.tls.certresolver: "cloudflare"
|
traefik.http.routers.radarr.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.radarr.middlewares: "voidauth@docker"
|
||||||
traefik.http.services.radarr.loadbalancer.server.port: "7878"
|
traefik.http.services.radarr.loadbalancer.server.port: "7878"
|
||||||
# Homepage
|
# Homepage
|
||||||
homepage.group: "Media"
|
homepage.group: "Media"
|
||||||
@@ -70,6 +74,7 @@ services:
|
|||||||
traefik.http.routers.sonarr.entrypoints: "https"
|
traefik.http.routers.sonarr.entrypoints: "https"
|
||||||
traefik.http.routers.sonarr.service: "sonarr"
|
traefik.http.routers.sonarr.service: "sonarr"
|
||||||
traefik.http.routers.sonarr.tls.certresolver: "cloudflare"
|
traefik.http.routers.sonarr.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.sonarr.middlewares: "voidauth@docker"
|
||||||
traefik.http.services.sonarr.loadbalancer.server.port: "8989"
|
traefik.http.services.sonarr.loadbalancer.server.port: "8989"
|
||||||
# Homepage
|
# Homepage
|
||||||
homepage.group: "Media"
|
homepage.group: "Media"
|
||||||
@@ -95,6 +100,7 @@ services:
|
|||||||
traefik.http.routers.prowlarr.rule: "Host(`prowlarr.${DOMAIN}`)"
|
traefik.http.routers.prowlarr.rule: "Host(`prowlarr.${DOMAIN}`)"
|
||||||
traefik.http.routers.prowlarr.entrypoints: "https"
|
traefik.http.routers.prowlarr.entrypoints: "https"
|
||||||
traefik.http.routers.prowlarr.service: "prowlarr"
|
traefik.http.routers.prowlarr.service: "prowlarr"
|
||||||
|
traefik.http.routers.prowlarr.middlewares: "voidauth@docker"
|
||||||
traefik.http.routers.prowlarr.tls.certresolver: "cloudflare"
|
traefik.http.routers.prowlarr.tls.certresolver: "cloudflare"
|
||||||
traefik.http.services.prowlarr.loadbalancer.server.port: "9696"
|
traefik.http.services.prowlarr.loadbalancer.server.port: "9696"
|
||||||
# Homepage
|
# Homepage
|
||||||
@@ -107,7 +113,6 @@ services:
|
|||||||
nzbget:
|
nzbget:
|
||||||
image: lscr.io/linuxserver/nzbget:latest
|
image: lscr.io/linuxserver/nzbget:latest
|
||||||
container_name: nzbget
|
container_name: nzbget
|
||||||
network_mode: service:gluetun
|
|
||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
@@ -123,6 +128,7 @@ services:
|
|||||||
traefik.http.routers.nzbget.entrypoints: "https"
|
traefik.http.routers.nzbget.entrypoints: "https"
|
||||||
traefik.http.routers.nzbget.service: "nzbget"
|
traefik.http.routers.nzbget.service: "nzbget"
|
||||||
traefik.http.routers.nzbget.tls.certresolver: "cloudflare"
|
traefik.http.routers.nzbget.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.nzbget.middlewares: "voidauth@docker"
|
||||||
traefik.http.services.nzbget.loadbalancer.server.port: "6789"
|
traefik.http.services.nzbget.loadbalancer.server.port: "6789"
|
||||||
# Homepage
|
# Homepage
|
||||||
homepage.group: "Media"
|
homepage.group: "Media"
|
||||||
@@ -130,6 +136,9 @@ services:
|
|||||||
homepage.icon: "nzbget.svg"
|
homepage.icon: "nzbget.svg"
|
||||||
homepage.href: "https://nzbget.${DOMAIN}"
|
homepage.href: "https://nzbget.${DOMAIN}"
|
||||||
homepage.description: "Usenet Downloader"
|
homepage.description: "Usenet Downloader"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
traefik_public:
|
traefik_public:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
32
media/audiobookshelf/docker-compose.yml
Normal file
32
media/audiobookshelf/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
audiobookshelf:
|
||||||
|
image: ghcr.io/advplyr/audiobookshelf:latest
|
||||||
|
container_name: audiobookshelf
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_PATH}/audiobookshelf/config:/config
|
||||||
|
- ${DOCKER_PATH}/audiobookshelf/metadata:/metadata
|
||||||
|
- ${MEDIA_PATH}/audiobooks:/audiobooks
|
||||||
|
- ${MEDIA_PATH}/podcasts:/podcasts
|
||||||
|
expose:
|
||||||
|
- 80
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.audiobookshelf.rule: "Host(`audiobookshelf.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.audiobookshelf.entrypoints: "https"
|
||||||
|
traefik.http.routers.audiobookshelf.service: "audiobookshelf"
|
||||||
|
traefik.http.routers.audiobookshelf.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.services.audiobookshelf.loadbalancer.server.port: "80"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
@@ -4,6 +4,11 @@ services:
|
|||||||
jellyfin:
|
jellyfin:
|
||||||
image: jellyfin/jellyfin
|
image: jellyfin/jellyfin
|
||||||
container_name: jellyfin
|
container_name: jellyfin
|
||||||
|
devices:
|
||||||
|
- /dev/dri/renderD128:/dev/dri/renderD128
|
||||||
|
- /dev/dri/card1:/dev/dri/card1
|
||||||
|
group_add:
|
||||||
|
- "992" # Replace this with your host's 'render' group ID
|
||||||
environment:
|
environment:
|
||||||
- PUID=1000
|
- PUID=1000
|
||||||
- PGID=1000
|
- PGID=1000
|
||||||
@@ -38,7 +43,7 @@ services:
|
|||||||
- PGID=1000
|
- PGID=1000
|
||||||
- TZ=Etc/UTC
|
- TZ=Etc/UTC
|
||||||
volumes:
|
volumes:
|
||||||
- ${DOCKER_PATH}/jellyseerr/config:/config
|
- ${DOCKER_PATH}/jellyseerr/config:/app/config
|
||||||
expose:
|
expose:
|
||||||
- 5055
|
- 5055
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
3
media/jellystat/.env.example
Normal file
3
media/jellystat/.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
JELLYSTAT_DB_USER=postgres
|
||||||
|
JELLYSTAT_DB_PASSWORD=change_me
|
||||||
|
JELLYSTAT_JWT_SECRET=change_me_to_a_random_string
|
||||||
52
media/jellystat/docker-compose.yml
Normal file
52
media/jellystat/docker-compose.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
jellystat-db:
|
||||||
|
image: postgres:15
|
||||||
|
container_name: jellystat-db
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: jfstat
|
||||||
|
POSTGRES_USER: ${JELLYSTAT_DB_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${JELLYSTAT_DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_PATH}/jellystat/postgres:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
jellystat:
|
||||||
|
image: cyfershepard/jellystat:latest
|
||||||
|
container_name: jellystat
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${JELLYSTAT_DB_USER:-postgres}
|
||||||
|
POSTGRES_PASSWORD: ${JELLYSTAT_DB_PASSWORD}
|
||||||
|
POSTGRES_IP: jellystat-db
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
JWT_SECRET: ${JELLYSTAT_JWT_SECRET}
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_PATH}/jellystat/backup:/app/backend/backup-data
|
||||||
|
depends_on:
|
||||||
|
- jellystat-db
|
||||||
|
expose:
|
||||||
|
- 3000
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.jellystat.rule: "Host(`jellystat.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.jellystat.entrypoints: "https"
|
||||||
|
traefik.http.routers.jellystat.service: "jellystat"
|
||||||
|
traefik.http.routers.jellystat.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.services.jellystat.loadbalancer.server.port: "3000"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Media"
|
||||||
|
homepage.name: "Jellystat"
|
||||||
|
homepage.icon: "jellystat.png"
|
||||||
|
homepage.href: "https://jellystat.${DOMAIN}"
|
||||||
|
homepage.description: "Jellyfin Statistics"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
36
media/jellysweep/docker-compose.yml
Normal file
36
media/jellysweep/docker-compose.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
jellysweep:
|
||||||
|
image: ghcr.io/jon4hz/jellysweep:latest
|
||||||
|
container_name: jellysweep
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_PATH}/jellysweep/config.yml:/app/config.yml:ro
|
||||||
|
- ${MEDIA_PATH}:/media
|
||||||
|
expose:
|
||||||
|
- 3002
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.jellysweep.rule: "Host(`jellysweep.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.jellysweep.entrypoints: "https"
|
||||||
|
traefik.http.routers.jellysweep.service: "jellysweep"
|
||||||
|
traefik.http.routers.jellysweep.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.services.jellysweep.loadbalancer.server.port: "3002"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Media"
|
||||||
|
homepage.name: "Jellysweep"
|
||||||
|
homepage.icon: "jellyfin.svg" # Using Jellyfin icon as placeholder
|
||||||
|
homepage.href: "https://jellysweep.${DOMAIN}"
|
||||||
|
homepage.description: "Jellyfin Cleanup Tool"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
14
media/jellysweep/example.config.yaml
Normal file
14
media/jellysweep/example.config.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
jellyfin:
|
||||||
|
url: "http://jellyfin:8096" # Internal docker DNS if on same network, or full URL
|
||||||
|
token: "YOUR_JELLYFIN_API_KEY"
|
||||||
|
|
||||||
|
# dry_run: true # Set to false to actually delete files
|
||||||
|
|
||||||
|
# Library configuration
|
||||||
|
libraries:
|
||||||
|
- name: Movies
|
||||||
|
keep: 1
|
||||||
|
age: 30d # Delete movies older than 30 days
|
||||||
|
- name: TV Shows
|
||||||
|
keep: 1
|
||||||
|
age: 30d
|
||||||
39
monitoring/beszel/docker-compose.yml
Normal file
39
monitoring/beszel/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
services:
|
||||||
|
beszel:
|
||||||
|
image: 'henrygd/beszel'
|
||||||
|
container_name: beszel
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/beszel/data:/beszel_data
|
||||||
|
labels:
|
||||||
|
homepage.group: "Monitoring"
|
||||||
|
homepage.name: "Beszel"
|
||||||
|
homepage.icon: "beszel.png"
|
||||||
|
homepage.href: "https://beszel.${DOMAIN}"
|
||||||
|
homepage.description: "Lightweight Server Monitoring"
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.beszel.rule: "Host(`beszel.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.beszel.entrypoints: "https"
|
||||||
|
traefik.http.routers.beszel.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.beszel.service: "beszel"
|
||||||
|
traefik.http.services.beszel.loadbalancer.server.port: "8090"
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
|
||||||
|
beszel-agent:
|
||||||
|
image: henrygd/beszel-agent
|
||||||
|
container_name: beszel-agent
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ${CONFIG_ROOT}/beszel_agent_data:/var/lib/beszel-agent
|
||||||
|
environment:
|
||||||
|
LISTEN: 45876
|
||||||
|
KEY: ${BESZEL_KEY}
|
||||||
|
TOKEN: ${BESZEL_TOKEN}
|
||||||
|
HUB_URL: https://beszel.${DOMAIN}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
32
monitoring/speedtest-tracker/docker-compose.yml
Normal file
32
monitoring/speedtest-tracker/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
services:
|
||||||
|
speedtest-tracker:
|
||||||
|
image: lscr.io/linuxserver/speedtest-tracker:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: speedtest-tracker
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- APP_KEY=${APP_KEY}
|
||||||
|
- DB_CONNECTION=sqlite
|
||||||
|
- SPEEDTEST_SCHEDULE=0 * * * *
|
||||||
|
- APP_TIMEZONE=Europe/London
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG_ROOT}/speedtest-tracker:/config
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
labels:
|
||||||
|
homepage.group: "Monitoring"
|
||||||
|
homepage.name: "Speedtest Tracker"
|
||||||
|
homepage.icon: "sh-speedtest-tracker.png"
|
||||||
|
homepage.href: "https://speedtest.${DOMAIN}"
|
||||||
|
homepage.description: "Internet speed tracking"
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.speedtest-tracker.rule: "Host(`speedtest.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.speedtest-tracker.entrypoints: "https"
|
||||||
|
traefik.http.routers.speedtest-tracker.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.routers.speedtest-tracker.service: "speedtest-tracker"
|
||||||
|
traefik.http.services.speedtest-tracker.loadbalancer.server.port: "80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
name: "Nginx Proxy Manager"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
|
||||||
restart: unless-stopped
|
|
||||||
labels:
|
|
||||||
homepage.group: "Proxies"
|
|
||||||
homepage.name: "Nginx Proxy Manager"
|
|
||||||
homepage.icon: "nginx-proxy-manager.png"
|
|
||||||
homepage.href: "http://npm:81"
|
|
||||||
homepage.description: "Reverse Proxy"
|
|
||||||
|
|
||||||
ports:
|
|
||||||
# These ports are in format <host-port>:<container-port>
|
|
||||||
- '80:80' # Public HTTP Port
|
|
||||||
- '443:443' # Public HTTPS Port
|
|
||||||
- '81:81' # Admin Web Port
|
|
||||||
# Add any other Stream port you want to expose
|
|
||||||
# - '21:21' # FTP
|
|
||||||
|
|
||||||
environment:
|
|
||||||
TZ: "Europe/London"
|
|
||||||
|
|
||||||
# Uncomment this if you want to change the location of
|
|
||||||
# the SQLite DB file within the container
|
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
|
||||||
|
|
||||||
# Uncomment this if IPv6 is not enabled on your host
|
|
||||||
# DISABLE_IPV6: 'true'
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- ${CONFIG_ROOT}/npm/data:/data
|
|
||||||
- ${CONFIG_ROOT}/npm/letsencrypt:/etc/letsencrypt
|
|
||||||
@@ -21,17 +21,18 @@ services:
|
|||||||
secrets:
|
secrets:
|
||||||
- cf_dns_api_token
|
- cf_dns_api_token
|
||||||
labels:
|
labels:
|
||||||
|
# Homepage
|
||||||
|
- "homepage.group=Proxies"
|
||||||
|
- "homepage.name=Traefik"
|
||||||
|
- "homepage.icon=traefik.svg"
|
||||||
|
- "homepage.href=https://traefik.${DOMAIN}"
|
||||||
|
- "homepage.description=Traefik Dashboard"
|
||||||
# Dashboard
|
# Dashboard
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.traefik.entrypoints=https"
|
- "traefik.http.routers.traefik.entrypoints=https"
|
||||||
- "traefik.http.routers.traefik.service=api@internal"
|
- "traefik.http.routers.traefik.service=api@internal"
|
||||||
- "traefik.http.routers.traefik.tls.certresolver=cloudflare"
|
- "traefik.http.routers.traefik.tls.certresolver=cloudflare"
|
||||||
# Authentik Forward Auth Middleware
|
|
||||||
- "traefik.http.middlewares.authentik.forwardauth.address=http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
|
||||||
- "traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true"
|
|
||||||
- "traefik.http.middlewares.authentik.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version"
|
|
||||||
- "traefik.http.routers.traefik.middlewares=authentik"
|
|
||||||
networks:
|
networks:
|
||||||
- traefik_public
|
- traefik_public
|
||||||
|
|
||||||
|
|||||||
39
security/frigate/docker-compose.yml
Normal file
39
security/frigate/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
frigate:
|
||||||
|
container_name: frigate
|
||||||
|
image: ghcr.io/blakeblackshear/frigate:stable
|
||||||
|
shm_size: "512mb" # Update based on camera resolution and count
|
||||||
|
privileged: true # Add this
|
||||||
|
devices:
|
||||||
|
- /dev/dri:/dev/dri # For Intel hardware acceleration
|
||||||
|
# - /dev/bus/usb:/dev/bus/usb # Google Coral USB
|
||||||
|
volumes:
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- ${CONFIG_ROOT}/frigate:/config
|
||||||
|
- ${CONFIG_ROOT}/frigate/storage:/media/frigate
|
||||||
|
- type: tmpfs
|
||||||
|
target: /tmp/cache
|
||||||
|
tmpfs:
|
||||||
|
size: 1000000000
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
- "8554:8554" # RTSP feeds
|
||||||
|
- "8555:8555/tcp" # WebRTC
|
||||||
|
- "8555:8555/udp" # WebRTC
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
# Traefik
|
||||||
|
traefik.enable: "true"
|
||||||
|
traefik.http.routers.frigate.rule: "Host(`frigate.${DOMAIN}`)"
|
||||||
|
traefik.http.routers.frigate.entrypoints: "https"
|
||||||
|
traefik.http.routers.frigate.service: "frigate"
|
||||||
|
traefik.http.routers.frigate.tls.certresolver: "cloudflare"
|
||||||
|
traefik.http.services.frigate.loadbalancer.server.port: "5000"
|
||||||
|
# Homepage
|
||||||
|
homepage.group: "Security"
|
||||||
|
homepage.name: "Frigate"
|
||||||
|
homepage.icon: "frigate.svg"
|
||||||
|
homepage.href: "https://frigate.${DOMAIN}"
|
||||||
|
homepage.description: "NVR with AI object detection"
|
||||||
Reference in New Issue
Block a user