Compare commits
4 Commits
fb31bcb0a3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fb3fc65e4 | ||
|
|
c546fab5e1 | ||
|
|
95bdab6b9d | ||
|
|
28aa6e28fe |
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
STORAGE_PATH=/mnt/storage/docker-data
|
STORAGE_PATH=/mnt/storage/docker-data
|
||||||
MEDIA_PATH=/mnt/storage
|
|
||||||
|
|
||||||
# User
|
# User
|
||||||
USER_UID=1000
|
USER_UID=1000
|
||||||
@@ -12,7 +11,6 @@ USER_GID=1000
|
|||||||
|
|
||||||
# Domain
|
# Domain
|
||||||
DOMAIN=sjhl.nz
|
DOMAIN=sjhl.nz
|
||||||
DOMAIN_WWW=__CHANGEME__
|
|
||||||
|
|
||||||
# Timezone
|
# Timezone
|
||||||
TZ=Pacific/Auckland
|
TZ=Pacific/Auckland
|
||||||
|
|||||||
@@ -7,3 +7,9 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
always_run: true
|
always_run: true
|
||||||
|
- id: build-docs-site
|
||||||
|
name: Build docs site
|
||||||
|
entry: make docs
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
always_run: true
|
||||||
|
|||||||
41
Makefile
41
Makefile
@@ -1,35 +1,50 @@
|
|||||||
SERVICES=traefik whoami gitea nextcloud devbox
|
SERVICES=traefik whoami gitea nextcloud devbox docs-site
|
||||||
|
SERVICES_DIR=services
|
||||||
|
|
||||||
.PHONY: up down restart backup init-env env-sync docs generate-docs serve-docs logs status
|
.PHONY: up down restart backup init-env env-sync docs generate-docs serve-docs logs status up-% down-%
|
||||||
|
|
||||||
up:
|
up:
|
||||||
@for svc in $(SERVICES); do \
|
for svc in $(SERVICES); do \
|
||||||
if [ -f "$$svc/docker-compose.yml" ]; then \
|
if [ -f "$(SERVICES_DIR)/$$svc/docker-compose.yml" ]; then \
|
||||||
echo "Starting $$svc..."; \
|
echo "Starting $$svc..."; \
|
||||||
(cd $$svc && docker compose up -d); \
|
(cd $(SERVICES_DIR)/$$svc && docker compose --env-file ../../.env `if [ -f .env ]; then echo --env-file .env; fi` up -d); \
|
||||||
fi; \
|
fi; \
|
||||||
done
|
done; \
|
||||||
|
|
||||||
|
up-%:
|
||||||
|
@svc=$*; \
|
||||||
|
if [ -f "$(SERVICES_DIR)/$$svc/docker-compose.yml" ]; then \
|
||||||
|
echo "Starting $$svc..."; \
|
||||||
|
(cd $(SERVICES_DIR)/$$svc && docker compose --env-file ../../.env `if [ -f .env ]; then echo --env-file .env; fi` up -d); \
|
||||||
|
fi
|
||||||
|
|
||||||
|
down-%:
|
||||||
|
@svc=$*; \
|
||||||
|
if [ -f "$(SERVICES_DIR)/$$svc/docker-compose.yml" ]; then \
|
||||||
|
echo "Stopping $$svc..."; \
|
||||||
|
(cd $(SERVICES_DIR)/$$svc && docker compose down); \
|
||||||
|
fi
|
||||||
|
|
||||||
down:
|
down:
|
||||||
@for svc in $(SERVICES); do \
|
@for svc in $(SERVICES); do \
|
||||||
if [ -f "$$svc/docker-compose.yml" ]; then \
|
if [ -f "$(SERVICES_DIR)/$$svc/docker-compose.yml" ]; then \
|
||||||
echo "Stopping $$svc..."; \
|
echo "Stopping $$svc..."; \
|
||||||
(cd $$svc && docker compose down); \
|
(cd $(SERVICES_DIR)/$$svc && docker compose down); \
|
||||||
fi; \
|
fi; \
|
||||||
done
|
done
|
||||||
|
|
||||||
restart: down up
|
restart: down up
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
@echo "=== Traefik ===" && (cd traefik && docker compose logs --tail=10)
|
@echo "=== Traefik ===" && (cd $(SERVICES_DIR)/traefik && docker compose logs --tail=10)
|
||||||
@echo "=== Gitea ===" && (cd gitea && docker compose logs --tail=10)
|
@echo "=== Gitea ===" && (cd $(SERVICES_DIR)/gitea && docker compose logs --tail=10)
|
||||||
@echo "=== Nextcloud ===" && (cd nextcloud && docker compose logs --tail=10)
|
@echo "=== Nextcloud ===" && (cd $(SERVICES_DIR)/nextcloud && docker compose logs --tail=10)
|
||||||
|
|
||||||
status:
|
status:
|
||||||
@for svc in $(SERVICES); do \
|
@for svc in $(SERVICES); do \
|
||||||
if [ -f "$$svc/docker-compose.yml" ]; then \
|
if [ -f "$(SERVICES_DIR)/$$svc/docker-compose.yml" ]; then \
|
||||||
echo "--- $$svc ---"; \
|
echo "--- $$svc ---"; \
|
||||||
(cd $$svc && docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"); \
|
(cd $(SERVICES_DIR)/$$svc && docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"); \
|
||||||
fi; \
|
fi; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
> ⚠️ **Early development**
|
||||||
|
>
|
||||||
|
> This project is in early development and not all features are working yet. Use with caution.
|
||||||
|
|
||||||
# homelab-infra
|
# homelab-infra
|
||||||
|
|
||||||
GitOps-managed infrastructure for homelab services using Docker Compose and Traefik for routing.
|
GitOps-managed infrastructure for homelab services using Docker Compose and Traefik for routing.
|
||||||
@@ -28,7 +32,7 @@ Currently includes: Gitea (Git hosting), Nextcloud (cloud storage), Devbox (deve
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Full documentation is available at the [MkDocs site](https://homelab.sjhl.nz) or build locally with `make docs`.
|
Full documentation is available at the [MkDocs site](https://docs.sjhl.nz) or build locally with `make docs`.
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
@@ -39,7 +43,8 @@ homelab-infra/
|
|||||||
├── docs/ # MkDocs documentation source
|
├── docs/ # MkDocs documentation source
|
||||||
├── mkdocs.yml # MkDocs configuration
|
├── mkdocs.yml # MkDocs configuration
|
||||||
├── scripts/ # Utility scripts
|
├── scripts/ # Utility scripts
|
||||||
└── [service]/ # Per-service configurations
|
└── services/ # Per-service configurations
|
||||||
|
└── [service]/
|
||||||
├── docker-compose.yml
|
├── docker-compose.yml
|
||||||
├── .env.example
|
├── .env.example
|
||||||
└── README.md
|
└── README.md
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# check_env_sync
|
|
||||||
|
|
||||||
> Python script documentation
|
|
||||||
|
|
||||||
::: scripts.check_env_sync
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# generate_docs
|
|
||||||
|
|
||||||
> Python script documentation
|
|
||||||
|
|
||||||
::: scripts.generate_docs
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# SJHL Documentation
|
# SJHL Documentation
|
||||||
|
|
||||||
|
!!! warning "Early development"
|
||||||
|
This documentation is in early development and not all features are working yet. Use with caution.
|
||||||
|
|
||||||
Welcome to Seirian & James' homelab documentation! This is a docs site that is built to easily show all the configs we use. MOst of the content is auto-generated from the actual config files, so it should always be up to date. This should be completely publically viewable as all private information is kept in `.env` files that are not committed to Git. It should provide good information on how to recover and rebuild the homelab if needed, and also just be a nice reference for how everything is configured.
|
Welcome to Seirian & James' homelab documentation! This is a docs site that is built to easily show all the configs we use. MOst of the content is auto-generated from the actual config files, so it should always be up to date. This should be completely publically viewable as all private information is kept in `.env` files that are not committed to Git. It should provide good information on how to recover and rebuild the homelab if needed, and also just be a nice reference for how everything is configured.
|
||||||
|
|
||||||
## Data Classification
|
## Data Classification
|
||||||
@@ -7,7 +10,7 @@ Welcome to Seirian & James' homelab documentation! This is a docs site that is b
|
|||||||
| Type | Example | Git Repo? | Backup? | Location |
|
| Type | Example | Git Repo? | Backup? | Location |
|
||||||
|------|---------|-----------|---------|----------|
|
|------|---------|-----------|---------|----------|
|
||||||
| **Source Configs** | `docker-compose.yml`, `.env.example`, `Makefile` | Yes | No | `/srv/homelab-infra` |
|
| **Source Configs** | `docker-compose.yml`, `.env.example`, `Makefile` | Yes | No | `/srv/homelab-infra` |
|
||||||
| **Service Secrets** | `.env` (DB passwords, API keys) | No | Yes | `/srv/homelab-infra/<service>/.env` |
|
| **Service Secrets** | `.env` (DB passwords, API keys) | No | Yes | `/srv/homelab-infra/services/<service>/.env` |
|
||||||
| **Runtime Configs** | `acme.json`, service configs | No | Yes | `/mnt/storage/docker-data/<service>` |
|
| **Runtime Configs** | `acme.json`, service configs | No | Yes | `/mnt/storage/docker-data/<service>` |
|
||||||
| **Persistent Data** | DB data, uploads, media | No | Yes | `/mnt/storage/docker-data/<service>` |
|
| **Persistent Data** | DB data, uploads, media | No | Yes | `/mnt/storage/docker-data/<service>` |
|
||||||
|
|
||||||
@@ -15,7 +18,9 @@ Welcome to Seirian & James' homelab documentation! This is a docs site that is b
|
|||||||
|
|
||||||
| Service | Image | Status |
|
| Service | Image | Status |
|
||||||
|---------|-------|--------|
|
|---------|-------|--------|
|
||||||
|
| [cloudflare-ddns](services/cloudflare-ddns.md) | `favonia/cloudflare-ddns:1` | active |
|
||||||
| [devbox](services/devbox.md) | `devbox-devcontainer` | active |
|
| [devbox](services/devbox.md) | `devbox-devcontainer` | active |
|
||||||
|
| [docs-site](services/docs-site.md) | `nginx:alpine` | active |
|
||||||
| [gitea](services/gitea.md) | `gitea/gitea:1.24.3` | active |
|
| [gitea](services/gitea.md) | `gitea/gitea:1.24.3` | active |
|
||||||
| [nextcloud](services/nextcloud.md) | `ghcr.io/nextcloud-releases/all-in-one:latest` | active |
|
| [nextcloud](services/nextcloud.md) | `ghcr.io/nextcloud-releases/all-in-one:latest` | active |
|
||||||
| [obsidian-livesync](services/obsidian-livesync.md) | `couchdb:latest` | active |
|
| [obsidian-livesync](services/obsidian-livesync.md) | `couchdb:latest` | active |
|
||||||
@@ -28,21 +33,21 @@ Welcome to Seirian & James' homelab documentation! This is a docs site that is b
|
|||||||
git clone git@gitea.sjhl.nz:james/homelab-infra.git
|
git clone git@gitea.sjhl.nz:james/homelab-infra.git
|
||||||
cd homelab-infra
|
cd homelab-infra
|
||||||
|
|
||||||
# Set up environment
|
# Create all missing .env files from templates
|
||||||
cp .env.example .env
|
make init-env
|
||||||
# Edit .env with real values
|
|
||||||
|
|
||||||
# Set up service secrets
|
# Edit .env and services/*/.env with real secrets
|
||||||
for svc in traefik gitea nextcloud qbittorrent jellyfin devbox obsidian; do
|
|
||||||
[ -f "$svc/.env.example" ] && cp "$svc/.env.example" "$svc/.env"
|
|
||||||
# Edit each .env with real secrets
|
|
||||||
done
|
|
||||||
|
|
||||||
# Create required Docker networks
|
# Create required Docker networks
|
||||||
docker network create web
|
docker network create web
|
||||||
|
|
||||||
# Start everything
|
# Start and verify everything
|
||||||
make up
|
make up
|
||||||
|
make status
|
||||||
|
|
||||||
|
# Optional: start/stop one service
|
||||||
|
# make up-<service>
|
||||||
|
# make down-<service>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
@@ -115,7 +120,11 @@ homelab-infra/
|
|||||||
├── docs/ # MkDocs documentation
|
├── docs/ # MkDocs documentation
|
||||||
├── mkdocs.yml # MkDocs config
|
├── mkdocs.yml # MkDocs config
|
||||||
├── scripts/ # Utility scripts
|
├── scripts/ # Utility scripts
|
||||||
└── [service subdirs]/ # All services have their own subdir
|
└── services/ # Service definitions
|
||||||
|
└── <service>/ # One folder per service
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── .env.example
|
||||||
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## Data Locations
|
## Data Locations
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
# Recovery Reference
|
|
||||||
|
|
||||||
This document was generated from `docker inspect` dumps taken after the accidental deletion of `/home/james`. It preserves the last known-good configuration state for rebuilding services.
|
|
||||||
|
|
||||||
## Container Summary
|
|
||||||
|
|
||||||
### traefik (Reverse Proxy)
|
|
||||||
- **Image:** `traefik:v3.6`
|
|
||||||
- **Config path (old):** `/home/james/apps/traefik/`
|
|
||||||
- **Volumes:** certs, dynamic config, docker.sock (ro)
|
|
||||||
- **Networks:** `web` (external)
|
|
||||||
- **Ports:** 80, 443, 8082->8080
|
|
||||||
- **Let's Encrypt:** HTTP challenge, email `letsencrypt.debug772@passmail.net`
|
|
||||||
|
|
||||||
### gitea
|
|
||||||
- **Image:** `gitea/gitea:1.24.3`
|
|
||||||
- **Data path (old):** `/mnt/storage/apps/gitea`
|
|
||||||
- **DB:** SQLite3
|
|
||||||
- **SSH:** port 222
|
|
||||||
- **URL:** `https://gitea.sjhl.nz/`
|
|
||||||
|
|
||||||
### nextcloud (AIO)
|
|
||||||
- **Image:** `ghcr.io/nextcloud-releases/all-in-one:latest`
|
|
||||||
- **Data path (old):** `/mnt/storage/apps/nextcloud`
|
|
||||||
- **Components:** mastercontainer, apache, nextcloud, database (PostgreSQL 17), redis, collabora, imaginary, whiteboard, notify-push
|
|
||||||
- **URL:** `https://nextcloud.sjhl.nz`
|
|
||||||
- **AIO mgmt:** port 8081
|
|
||||||
- **Apache port:** 11000
|
|
||||||
|
|
||||||
### qbittorrent + VPN
|
|
||||||
- **VPN:** ProtonVPN WireGuard, New Zealand, port forwarding
|
|
||||||
- **Data path (old):** `/home/james/apps/qbittorrent/`
|
|
||||||
- **Downloads:** `/mnt/storage/torrents`
|
|
||||||
- **Components:** gluetun, qbittorrent, jackett
|
|
||||||
- **Ports:** 8080 (qBittorrent), 9117 (Jackett)
|
|
||||||
|
|
||||||
### jellyfin
|
|
||||||
- **Image:** `jellyfin/jellyfin:10.10.7`
|
|
||||||
- **Config path (old):** `/home/james/apps/jellyfin-config`
|
|
||||||
- **Media:** `/mnt/storage`
|
|
||||||
- **Port:** 8096
|
|
||||||
- **Network:** default bridge (NOT on `web` network)
|
|
||||||
- **Note:** Was not behind Traefik in old setup
|
|
||||||
|
|
||||||
### devbox
|
|
||||||
- **Image:** `devbox-devcontainer` (local build)
|
|
||||||
- **Data path (old):** `/mnt/storage/apps/devbox`
|
|
||||||
- **Port:** 46573->2222 (SSH)
|
|
||||||
- **Memory limit:** 10GB, swap 20GB
|
|
||||||
|
|
||||||
### obsidian-livesync
|
|
||||||
- **Image:** `couchdb:latest`
|
|
||||||
- **Status:** Exited (127) at time of backup
|
|
||||||
- **Note:** No inspect data captured, container was stopped
|
|
||||||
|
|
||||||
## Recovery Path Mapping
|
|
||||||
|
|
||||||
| Old Path | New Path |
|
|
||||||
|----------|----------|
|
|
||||||
| `/home/james/apps/traefik/` | `/mnt/storage/docker-data/traefik/` |
|
|
||||||
| `/home/james/apps/qbittorrent/` | `/mnt/storage/docker-data/qbittorrent/` |
|
|
||||||
| `/home/james/apps/jellyfin-config` | `/mnt/storage/docker-data/jellyfin/config` |
|
|
||||||
| `/mnt/storage/apps/gitea` | `/mnt/storage/docker-data/gitea` |
|
|
||||||
| `/mnt/storage/apps/nextcloud` | `/mnt/storage/docker-data/nextcloud` |
|
|
||||||
| `/mnt/storage/apps/devbox` | `/mnt/storage/docker-data/devbox` |
|
|
||||||
| `/mnt/storage/torrents` | `/mnt/storage/torrents` (unchanged) |
|
|
||||||
15
docs/services/cloudflare-ddns.md
Normal file
15
docs/services/cloudflare-ddns.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# cloudflare-ddns
|
||||||
|
|
||||||
|
|
||||||
|
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
||||||
|
## Docker Compose Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
--8<-- "services/cloudflare-ddns/docker-compose.yml"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
--8<-- "services/cloudflare-ddns/.env.example"
|
||||||
|
```
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "devbox/docker-compose.yml"
|
--8<-- "services/devbox/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables (`.env.example`)
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
--8<-- "devbox/.env.example"
|
--8<-- "services/devbox/.env.example"
|
||||||
```
|
```
|
||||||
|
|||||||
10
docs/services/docs-site.md
Normal file
10
docs/services/docs-site.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# docs-site
|
||||||
|
|
||||||
|
|
||||||
|
--8<-- "services/docs-site/README.md"
|
||||||
|
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
||||||
|
## Docker Compose Configuration
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
--8<-- "services/docs-site/docker-compose.yml"
|
||||||
|
```
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "gitea/docker-compose.yml"
|
--8<-- "services/gitea/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables (`.env.example`)
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
--8<-- "gitea/.env.example"
|
--8<-- "services/gitea/.env.example"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "nextcloud/docker-compose.yml"
|
--8<-- "services/nextcloud/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables (`.env.example`)
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
--8<-- "nextcloud/.env.example"
|
--8<-- "services/nextcloud/.env.example"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
# obsidian-livesync
|
# obsidian-livesync
|
||||||
|
|
||||||
|
|
||||||
|
--8<-- "services/obsidian-livesync/README.md"
|
||||||
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "obsidian-livesync/docker-compose.yml"
|
--8<-- "services/obsidian-livesync/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables (`.env.example`)
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
--8<-- "obsidian-livesync/.env.example"
|
--8<-- "services/obsidian-livesync/.env.example"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# traefik
|
# traefik
|
||||||
|
|
||||||
|
|
||||||
--8<-- "traefik/README.md"
|
--8<-- "services/traefik/README.md"
|
||||||
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.
|
||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "traefik/docker-compose.yml"
|
--8<-- "services/traefik/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment Variables (`.env.example`)
|
## Environment Variables (`.env.example`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
--8<-- "traefik/.env.example"
|
--8<-- "services/traefik/.env.example"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,5 +5,5 @@
|
|||||||
## Docker Compose Configuration
|
## Docker Compose Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
--8<-- "whoami/docker-compose.yml"
|
--8<-- "services/whoami/docker-compose.yml"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ markdown_extensions:
|
|||||||
- .
|
- .
|
||||||
nav:
|
nav:
|
||||||
- Home: index.md
|
- Home: index.md
|
||||||
- Recovery Reference: recovery.md
|
|
||||||
- Services:
|
- Services:
|
||||||
|
- Cloudflare Ddns: services/cloudflare-ddns.md
|
||||||
- Devbox: services/devbox.md
|
- Devbox: services/devbox.md
|
||||||
|
- Docs Site: services/docs-site.md
|
||||||
- Gitea: services/gitea.md
|
- Gitea: services/gitea.md
|
||||||
- Nextcloud: services/nextcloud.md
|
- Nextcloud: services/nextcloud.md
|
||||||
- Obsidian Livesync: services/obsidian-livesync.md
|
- Obsidian Livesync: services/obsidian-livesync.md
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
services:
|
|
||||||
couchdb:
|
|
||||||
image: couchdb:latest
|
|
||||||
container_name: obsidian-livesync
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "${COUCHDB_PORT:-5984}:5984"
|
|
||||||
volumes:
|
|
||||||
- ${STORAGE_PATH}/obsidian:/opt/couchdb/data
|
|
||||||
env_file:
|
|
||||||
- ../.env
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
- COUCHDB_USER=${COUCHDB_USER:-admin}
|
|
||||||
- COUCHDB_PASSWORD=${COUCHDB_PASSWORD}
|
|
||||||
@@ -4,12 +4,11 @@ Auto-generate markdown docs from docker-compose.yml files.
|
|||||||
Usage: python3 scripts/generate_docs.py
|
Usage: python3 scripts/generate_docs.py
|
||||||
"""
|
"""
|
||||||
import yaml
|
import yaml
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).parent.parent
|
REPO_ROOT = Path(__file__).parent.parent
|
||||||
SERVICES_DIR = REPO_ROOT
|
SERVICES_DIR = REPO_ROOT / "services"
|
||||||
DOCS_DIR = REPO_ROOT / "docs" / "services"
|
DOCS_DIR = REPO_ROOT / "docs" / "services"
|
||||||
|
|
||||||
DISPLAY_NAME_OVERRIDES = {
|
DISPLAY_NAME_OVERRIDES = {
|
||||||
@@ -175,6 +174,8 @@ def format_security(sec):
|
|||||||
|
|
||||||
def generate_service_md(service_name, service_dir):
|
def generate_service_md(service_name, service_dir):
|
||||||
"""Generate service doc that includes docker-compose.yml, Dockerfile, README, and .env.example."""
|
"""Generate service doc that includes docker-compose.yml, Dockerfile, README, and .env.example."""
|
||||||
|
include_base = f"services/{service_dir.name}"
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
lines.append(f"# {service_name}")
|
lines.append(f"# {service_name}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
@@ -183,7 +184,7 @@ def generate_service_md(service_name, service_dir):
|
|||||||
# Include README.md first if exists
|
# Include README.md first if exists
|
||||||
readme_path = service_dir / "README.md"
|
readme_path = service_dir / "README.md"
|
||||||
if readme_path.exists():
|
if readme_path.exists():
|
||||||
lines.append(f'--8<-- "{service_dir.name}/README.md"')
|
lines.append(f'--8<-- "{include_base}/README.md"')
|
||||||
|
|
||||||
lines.append("> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.")
|
lines.append("> Below are the configuration files for this service. For details on how to deploy or customize, refer to the README above or the official documentation for the service.")
|
||||||
# Include docker-compose.yml
|
# Include docker-compose.yml
|
||||||
@@ -192,7 +193,7 @@ def generate_service_md(service_name, service_dir):
|
|||||||
lines.append("## Docker Compose Configuration")
|
lines.append("## Docker Compose Configuration")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("```yaml")
|
lines.append("```yaml")
|
||||||
lines.append(f'--8<-- "{service_dir.name}/docker-compose.yml"')
|
lines.append(f'--8<-- "{include_base}/docker-compose.yml"')
|
||||||
lines.append("```")
|
lines.append("```")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -202,7 +203,7 @@ def generate_service_md(service_name, service_dir):
|
|||||||
lines.append("## Dockerfile")
|
lines.append("## Dockerfile")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("```dockerfile")
|
lines.append("```dockerfile")
|
||||||
lines.append(f'--8<-- "{service_dir.name}/Dockerfile"')
|
lines.append(f'--8<-- "{include_base}/Dockerfile"')
|
||||||
lines.append("```")
|
lines.append("```")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -212,7 +213,7 @@ def generate_service_md(service_name, service_dir):
|
|||||||
lines.append("## Environment Variables (`.env.example`)")
|
lines.append("## Environment Variables (`.env.example`)")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("```bash")
|
lines.append("```bash")
|
||||||
lines.append(f'--8<-- "{service_dir.name}/.env.example"')
|
lines.append(f'--8<-- "{include_base}/.env.example"')
|
||||||
lines.append("```")
|
lines.append("```")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
@@ -275,6 +276,10 @@ def cleanup_stale_service_docs(service_names):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
if not SERVICES_DIR.exists():
|
||||||
|
print(f"Services directory not found: {SERVICES_DIR}", file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
all_services = {}
|
all_services = {}
|
||||||
|
|
||||||
# Find all service directories with docker-compose.yml
|
# Find all service directories with docker-compose.yml
|
||||||
@@ -284,9 +289,6 @@ def main():
|
|||||||
compose_file = item / "docker-compose.yml"
|
compose_file = item / "docker-compose.yml"
|
||||||
if not compose_file.exists():
|
if not compose_file.exists():
|
||||||
continue
|
continue
|
||||||
# Skip scripts/docs directories
|
|
||||||
if item.name in ("scripts", "docs", "__pycache__"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
service_name = item.name
|
service_name = item.name
|
||||||
try:
|
try:
|
||||||
|
|||||||
6
services/cloudflare-ddns/.env.example
Normal file
6
services/cloudflare-ddns/.env.example
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# cloudflare-ddns/.env
|
||||||
|
# Copy to .env and fill in real values. NEVER commit .env.
|
||||||
|
|
||||||
|
CLOUDFLARE_API_TOKEN=__CHANGEME__
|
||||||
|
DOMAINS=example.org,www.example.org
|
||||||
|
PROXIED=true
|
||||||
34
services/cloudflare-ddns/docker-compose.yml
Normal file
34
services/cloudflare-ddns/docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
services:
|
||||||
|
cloudflare-ddns:
|
||||||
|
image: favonia/cloudflare-ddns:1
|
||||||
|
# Prefer "1" or "1.x.y" in production.
|
||||||
|
#
|
||||||
|
# - "1" tracks the latest stable release whose major version is 1
|
||||||
|
# - "1.x.y" pins one specific stable version
|
||||||
|
# - "latest" moves to each new stable release and may pick up breaking
|
||||||
|
# changes in a future major release, so it is not recommended in production
|
||||||
|
# - "edge" tracks the latest unreleased development build
|
||||||
|
network_mode: host
|
||||||
|
# Optional. This bypasses network isolation and makes IPv6 easier.
|
||||||
|
# See "Use IPv6 without sharing the host network".
|
||||||
|
restart: always
|
||||||
|
# Restart the updater after reboot
|
||||||
|
user: "1000:1000"
|
||||||
|
# Run the updater with specific user and group IDs (in that order).
|
||||||
|
# You can change the two numbers based on your need.
|
||||||
|
read_only: true
|
||||||
|
# Make the container filesystem read-only (optional but recommended)
|
||||||
|
cap_drop: [all]
|
||||||
|
# Drop all Linux capabilities (optional but recommended)
|
||||||
|
security_opt: [no-new-privileges:true]
|
||||||
|
# Another protection to restrict superuser privileges (optional but recommended)
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
|
||||||
|
# Your Cloudflare API token
|
||||||
|
- DOMAINS=${DOMAINS}
|
||||||
|
# Your domains (separated by commas)
|
||||||
|
- PROXIED=${PROXIED:-true}
|
||||||
|
# Leaning toward using Cloudflare's proxy for these domains (optional)
|
||||||
|
# Existing DNS records in Cloudflare keep their current proxy statuses
|
||||||
7
services/docs-site/README.md
Normal file
7
services/docs-site/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Serves the generated MkDocs static output from `../site` using NGINX.
|
||||||
|
|
||||||
|
Routing is handled by Traefik with host `docs.${DOMAIN}`.
|
||||||
|
|
||||||
|
Deploy flow:
|
||||||
|
1. Build docs: `make docs`
|
||||||
|
2. Start container: `make up-docs-site` (or `make up`)
|
||||||
20
services/docs-site/docker-compose.yml
Normal file
20
services/docs-site/docker-compose.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
docs-site:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: docs-site
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ../site:/usr/share/nginx/html:ro
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.docs-site.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.docs-site.rule=Host(`docs.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.docs-site.tls=true"
|
||||||
|
- "traefik.http.routers.docs-site.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.docs-site.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
container_name: nextcloud-aio-mastercontainer
|
container_name: nextcloud-aio-mastercontainer
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "${AIO_PORT:-8081}:8080"
|
- "${AIO_PORT}:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config
|
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
@@ -15,8 +15,6 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- nextcloud-aio
|
- nextcloud-aio
|
||||||
- web
|
- web
|
||||||
env_file:
|
|
||||||
- ../.env
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
nextcloud_aio_mastercontainer:
|
nextcloud_aio_mastercontainer:
|
||||||
@@ -25,3 +23,5 @@ networks:
|
|||||||
nextcloud-aio:
|
nextcloud-aio:
|
||||||
web:
|
web:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
|
|
||||||
@@ -3,4 +3,3 @@
|
|||||||
|
|
||||||
COUCHDB_USER=admin
|
COUCHDB_USER=admin
|
||||||
COUCHDB_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
|
COUCHDB_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
|
||||||
COUCHDB_PORT=5984
|
|
||||||
1
services/obsidian-livesync/README.md
Normal file
1
services/obsidian-livesync/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*Note*: When setting up for the first time you need to create the directories for both the etc and the data and make sure they are owned by the same UID as the CouchDB container (default 5984) to avoid permission issues.
|
||||||
38
services/obsidian-livesync/docker-compose.yml
Executable file
38
services/obsidian-livesync/docker-compose.yml
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
services:
|
||||||
|
couchdb:
|
||||||
|
image: couchdb:latest
|
||||||
|
container_name: obsidian-livesync
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- COUCHDB_USER=${COUCHDB_USER}
|
||||||
|
- COUCHDB_PASSWORD=${COUCHDB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ${STORAGE_PATH}/obsidian-livesync/data:/opt/couchdb/data
|
||||||
|
- ${STORAGE_PATH}/obsidian-livesync/local.ini:/opt/couchdb/etc/local.d
|
||||||
|
ports:
|
||||||
|
- 5984:5984
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
# The Traefik Network
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
# Don't forget to replace 'obsidian-livesync.example.org' with your own domain
|
||||||
|
- "traefik.http.routers.obsidian-livesync.rule=Host(`obsidian-livesync.${DOMAIN}`)"
|
||||||
|
# The 'websecure' entryPoint is basically your HTTPS entrypoint. Check the next code snippet if you are encountering problems only; you probably have a working traefik configuration if this is not your first container you are reverse proxying.
|
||||||
|
- "traefik.http.routers.obsidian-livesync.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.obsidian-livesync.service=obsidian-livesync"
|
||||||
|
- "traefik.http.services.obsidian-livesync.loadbalancer.server.port=5984"
|
||||||
|
- "traefik.http.routers.obsidian-livesync.tls=true"
|
||||||
|
# Replace the string 'letsencrypt' with your own certificate resolver
|
||||||
|
- "traefik.http.routers.obsidian-livesync.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.accessControlAllowOriginList=app://obsidian.md,capacitor://localhost,http://localhost"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.accessControlAllowMethods=GET,POST,PUT,DELETE,OPTIONS,HEAD"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.accessControlAllowHeaders=Authorization,Content-Type,Accept,Origin,X-Requested-With"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.accessControlAllowCredentials=true"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.accessControlMaxAge=86400"
|
||||||
|
- "traefik.http.middlewares.obsidiancors.headers.addVaryHeader=true"
|
||||||
|
- "traefik.http.routers.obsidian-livesync.middlewares=obsidiancors"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# traefik/.env
|
# traefik/.env
|
||||||
# Copy to .env and fill in real values. NEVER commit .env.
|
# Copy to .env and fill in real values. NEVER commit .env.
|
||||||
|
|
||||||
TRAEFIK_DASHBOARD_PORT=8082
|
TRAEFIK_DASHBOARD_PORT=8080
|
||||||
ACME_EMAIL=letsencrypt@example.com
|
ACME_EMAIL=letsencrypt@example.com
|
||||||
DASHBOARD_BASIC_AUTH=admin:$$apr1$$changeme$$REPLACE_WITH_HTPASSWD_HASH
|
DASHBOARD_BASIC_AUTH=admin:$$apr1$$changeme$$REPLACE_WITH_HTPASSWD_HASH
|
||||||
1
services/traefik/README.md
Normal file
1
services/traefik/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is the core proxy that protects my server. It handles SSL termination, routing, and the dashboard for monitoring. I use Traefik's Docker provider to automatically discover services and route traffic based on labels in their `docker-compose.yml` files.
|
||||||
@@ -6,11 +6,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
- "${TRAEFIK_DASHBOARD_PORT:-8082}:8080"
|
- "${TRAEFIK_DASHBOARD_PORT}:8080"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- ${STORAGE_PATH}/traefik/certs:/certs:rw
|
- ${STORAGE_PATH}/traefik/certs:/certs:rw
|
||||||
- ${STORAGE_PATH}/traefik/dynamic:/dynamic:ro
|
- ./dynamic:/dynamic:ro
|
||||||
|
- ${STORAGE_PATH}/traefik/letsencrypt:/letsencrypt
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
security_opt:
|
security_opt:
|
||||||
@@ -23,6 +24,7 @@ services:
|
|||||||
- "traefik.http.routers.dashboard.rule=Host(`dashboard.${DOMAIN}`)"
|
- "traefik.http.routers.dashboard.rule=Host(`dashboard.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.dashboard.service=api@internal"
|
- "traefik.http.routers.dashboard.service=api@internal"
|
||||||
- "traefik.http.routers.dashboard.tls=true"
|
- "traefik.http.routers.dashboard.tls=true"
|
||||||
|
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
|
||||||
command:
|
command:
|
||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
|
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
|
||||||
@@ -31,6 +33,8 @@ services:
|
|||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
- "--providers.docker.exposedbydefault=false"
|
- "--providers.docker.exposedbydefault=false"
|
||||||
- "--providers.docker.network=web"
|
- "--providers.docker.network=web"
|
||||||
|
- "--api.dashboard=true"
|
||||||
|
- "--api.insecure=false"
|
||||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
||||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||||
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
|
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
|
||||||
@@ -40,6 +44,9 @@ services:
|
|||||||
- "--providers.file.directory=/dynamic"
|
- "--providers.file.directory=/dynamic"
|
||||||
- "--providers.file.watch=true"
|
- "--providers.file.watch=true"
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- DOMAIN=${DOMAIN}
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
web:
|
web:
|
||||||
external: true
|
external: true
|
||||||
38
services/traefik/dynamic/nextcloud.yml
Normal file
38
services/traefik/dynamic/nextcloud.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
http:
|
||||||
|
routers:
|
||||||
|
nextcloud:
|
||||||
|
rule: 'Host(`nextcloud.{{ env "DOMAIN" }}`)'
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
service: nextcloud
|
||||||
|
middlewares:
|
||||||
|
- nextcloud-chain
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
services:
|
||||||
|
nextcloud:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://nextcloud-aio-apache:11000"
|
||||||
|
|
||||||
|
middlewares:
|
||||||
|
nextcloud-secure-headers:
|
||||||
|
headers:
|
||||||
|
hostsProxyHeaders:
|
||||||
|
- X-Forwarded-Host
|
||||||
|
customRequestHeaders:
|
||||||
|
X-Forwarded-Proto: https
|
||||||
|
referrerPolicy: same-origin
|
||||||
|
|
||||||
|
nextcloud-dav:
|
||||||
|
redirectRegex:
|
||||||
|
regex: "^https://([^/]+)/.well-known/(card|cal)dav"
|
||||||
|
replacement: "https://${1}/remote.php/dav/"
|
||||||
|
permanent: true
|
||||||
|
|
||||||
|
nextcloud-chain:
|
||||||
|
chain:
|
||||||
|
middlewares:
|
||||||
|
- nextcloud-dav
|
||||||
|
- nextcloud-secure-headers
|
||||||
4
services/traefik/dynamic/tls.yaml
Normal file
4
services/traefik/dynamic/tls.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
tls:
|
||||||
|
certificates:
|
||||||
|
- certFile: /certs/local.crt
|
||||||
|
keyFile: /certs/local.key
|
||||||
@@ -10,6 +10,7 @@ services:
|
|||||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||||
- "traefik.http.routers.whoami.rule=Host(`whoami.${DOMAIN}`)"
|
- "traefik.http.routers.whoami.rule=Host(`whoami.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.whoami.tls=true"
|
- "traefik.http.routers.whoami.tls=true"
|
||||||
|
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
web:
|
web:
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
THis is the project
|
|
||||||
|
|
||||||
More information hear.
|
|
||||||
Reference in New Issue
Block a user