Compare commits
39 Commits
11ceb46e4a
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b69951f917 | |||
| d7829cb5ae | |||
| 8ae9c9e878 | |||
| fcff6f3298 | |||
| e3cd2df372 | |||
| 067d017ea6 | |||
| 3251f27edb | |||
| cf32e669cb | |||
| e613d766ec | |||
| 53608a7195 | |||
| 735adfabea | |||
| a9eb3de90d | |||
| c27a012d54 | |||
| d367540eac | |||
| c0aa3acbfc | |||
| d9bbfea9c4 | |||
| 27c9e1c8a5 | |||
| d4327bd152 | |||
| 2130069836 | |||
| 6eb603205a | |||
| f231dd5432 | |||
| afec1c03f5 | |||
| 9c7a34e9c0 | |||
| 40dfe5776a | |||
| e5e6a2c302 | |||
| 9c65cfc9e3 | |||
| 15ce2e3f4b | |||
| 300c685d50 | |||
| 5c398ee77c | |||
| 2e31c1dcc9 | |||
| 6f12bf9af7 | |||
| 758e69300f | |||
| ae81935376 | |||
| d8cfcd23d1 | |||
| a1f0f7091b | |||
| 31841d1ac3 | |||
| 4c522961a9 | |||
| cb241c9696 | |||
| 4b99e21a99 |
@@ -0,0 +1,6 @@
|
|||||||
|
# Webhook for update/backup notifications (n8n → Telegram)
|
||||||
|
WEBHOOK_URL=https://n8n.example.com/webhook/your-webhook-id
|
||||||
|
|
||||||
|
# Restic backup repository and password
|
||||||
|
RESTIC_REPOSITORY=/mnt/hidrive/users/valknar/Backup/stacks
|
||||||
|
RESTIC_PASSWORD=changeme
|
||||||
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
.claude
|
.claude
|
||||||
.env
|
.env
|
||||||
*.sql
|
*.sql
|
||||||
*.log
|
*.log
|
||||||
|
.data/
|
||||||
@@ -10,20 +10,81 @@ Each stack is independently deployable with its own `compose.yml` and `.env`. Al
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `traefik` | Reverse proxy, TLS termination | traefik |
|
| `traefik` | Reverse proxy, TLS termination | traefik |
|
||||||
| `mailpit` | SMTP relay (no web UI) | mailpit |
|
| `mailpit` | SMTP relay (no web UI) | mailpit |
|
||||||
| `watchtower` | Automatic container updates | watchtower |
|
|
||||||
| `umami` | Web analytics | umami, db |
|
| `umami` | Web analytics | umami, db |
|
||||||
| `immich` | Photo & video management | immich, ml, redis, db |
|
| `immich` | Photo & video management | immich, ml, redis, db |
|
||||||
| `blinko` | AI-powered personal notes | blinko, db |
|
|
||||||
| `n8n` | Workflow automation & notification relay | n8n, db |
|
| `n8n` | Workflow automation & notification relay | n8n, db |
|
||||||
| `gitea` | Git hosting + CI runner | gitea, runner, db |
|
| `gitea` | Git hosting + CI runner | gitea, runner, db |
|
||||||
| `coolify` | Deployment platform | coolify, realtime, redis, db |
|
| `coolify` | Deployment platform | coolify, realtime, redis, db |
|
||||||
| `vaultwarden` | Password manager | vaultwarden |
|
| `passbolt` | Password manager (GPG-encrypted, team sharing) | passbolt, db |
|
||||||
|
| `code` | Browser-based VS Code IDE with Anthropic API access | code |
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
| Directory | Description |
|
| File | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `_backup` | Daily restic backups to HiDrive (host script + systemd timer) |
|
| `stacks.sh` | CLI to manage stacks, services, scaffolding, updates, and backups |
|
||||||
|
| `.env` | Root config: `WEBHOOK_URL`, `RESTIC_REPOSITORY`, `RESTIC_PASSWORD` (gitignored) |
|
||||||
|
| `.env.example` | Template for the root `.env` |
|
||||||
|
|
||||||
|
## stacks.sh
|
||||||
|
|
||||||
|
`stacks.sh` is the primary management CLI. It wraps `docker compose` with glob-based multi-stack targeting, manages the update and backup systemd services, scaffolds new stacks, and generates shell completions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stacks.sh help
|
||||||
|
```
|
||||||
|
|
||||||
|
**Stack commands** — all accept one or more stack names or glob patterns (omit for all stacks):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stacks.sh ls # list all stacks with live container status
|
||||||
|
./stacks.sh ps gitea # container status table
|
||||||
|
./stacks.sh up # start all stacks
|
||||||
|
./stacks.sh up gitea traefik # start specific stacks
|
||||||
|
./stacks.sh down 'g*' # stop stacks matching glob
|
||||||
|
./stacks.sh restart 'g*,traefik' # glob + exact name, comma-separated
|
||||||
|
./stacks.sh pull --parallel # pull all images in parallel
|
||||||
|
./stacks.sh logs -f gitea # follow logs
|
||||||
|
./stacks.sh logs -n 100 gitea n8n # tail multiple stacks
|
||||||
|
./stacks.sh exec gitea gitea gitea admin user list # exec in container
|
||||||
|
./stacks.sh run passbolt passbolt bin/cake passbolt healthcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**Service management** (reads `WEBHOOK_URL`, `RESTIC_REPOSITORY`, `RESTIC_PASSWORD` from root `.env`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stacks.sh update install # write & enable systemd update timer
|
||||||
|
./stacks.sh update run # run update now
|
||||||
|
./stacks.sh update status # show timer/service status
|
||||||
|
./stacks.sh update logs # show journal logs
|
||||||
|
|
||||||
|
./stacks.sh backup install # write & enable systemd backup timer
|
||||||
|
./stacks.sh backup run # run backup now (auto-detects <stack>_db containers)
|
||||||
|
./stacks.sh backup snapshots # list restic snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
**Scaffold a new stack:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stacks.sh new myapp # basic stack with Traefik labels
|
||||||
|
./stacks.sh new myapp --db postgres # with Postgres service
|
||||||
|
./stacks.sh new myapp --db postgres --redis # with Postgres + Redis
|
||||||
|
./stacks.sh new myapp --no-traefik # expose port instead of Traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
Generates `compose.yml` (with healthchecks, `../.data/` volumes, Traefik labels) and `.env.example`.
|
||||||
|
|
||||||
|
**Shell completion:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dynamic — discovers stacks at tab-complete time (default)
|
||||||
|
./stacks.sh completion zsh --install
|
||||||
|
|
||||||
|
# Static — bakes current stack list in; useful on the VPS
|
||||||
|
./stacks.sh completion zsh --static --install
|
||||||
|
```
|
||||||
|
|
||||||
|
**Global flags:** `--dry-run`, `--parallel`, `--verbose`, `--quiet`
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
@@ -44,39 +105,36 @@ All stacks share the external `falcon_network` Docker network for inter-service
|
|||||||
|
|
||||||
## Backup
|
## Backup
|
||||||
|
|
||||||
The `_backup` stack runs a daily restic backup at 3:00 AM. It dumps all Postgres databases, then backs up the entire `.data/` directory to HiDrive. Retention: 7 daily, 4 weekly, 6 monthly snapshots. Notifications go to Telegram via n8n.
|
Runs daily at 3:00 AM via a systemd timer. Detects Postgres databases automatically by convention (`<stack>_db` container, user `<stack>`, database `<stack>`), dumps each one, then runs a full restic backup of `.data/`. Retention: 7 daily, 4 weekly, 6 monthly. Notifications go to Telegram via n8n.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Deploy backup stack
|
# First-time setup on VPS
|
||||||
rsync -avz _backup/ vps:~/stacks/_backup/
|
cp .env.example .env && $EDITOR .env # set RESTIC_REPOSITORY, RESTIC_PASSWORD, WEBHOOK_URL
|
||||||
|
source .env && restic init # initialise restic repo
|
||||||
|
|
||||||
# Initialize restic repo (first time only)
|
./stacks.sh backup install # write & enable systemd unit + timer
|
||||||
ssh vps 'source ~/stacks/_backup/.env && restic init -r /mnt/hidrive/users/valknar/Backup/stacks'
|
./stacks.sh backup run # test run
|
||||||
|
./stacks.sh backup snapshots # list snapshots
|
||||||
|
./stacks.sh backup status # timer/service status
|
||||||
|
./stacks.sh backup logs # journald logs
|
||||||
|
```
|
||||||
|
|
||||||
# Install systemd units
|
## Updates
|
||||||
ssh vps 'ln -sf ~/stacks/_backup/stacks-backup.service /etc/systemd/system/ && \
|
|
||||||
ln -sf ~/stacks/_backup/stacks-backup.timer /etc/systemd/system/ && \
|
|
||||||
systemctl daemon-reload && systemctl enable --now stacks-backup.timer'
|
|
||||||
|
|
||||||
# Manual test run
|
Runs nightly at 2:00 AM via a systemd timer. Pulls the latest image for every stack, recreates any container whose image changed, prunes dangling images, and sends a Telegram notification via n8n.
|
||||||
ssh vps '~/stacks/_backup/backup.sh'
|
|
||||||
|
|
||||||
# Check timer status
|
```bash
|
||||||
ssh vps 'systemctl status stacks-backup.timer'
|
./stacks.sh update install # write & enable systemd unit + timer
|
||||||
|
./stacks.sh update run # test run
|
||||||
# View snapshots
|
./stacks.sh update status # timer/service status
|
||||||
ssh vps 'source ~/stacks/_backup/.env && restic -r /mnt/hidrive/users/valknar/Backup/stacks snapshots'
|
./stacks.sh update logs # journald logs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Notifications
|
## Notifications
|
||||||
|
|
||||||
Watchtower and the backup script both POST to an n8n webhook, which forwards messages to Telegram.
|
Both update and backup POST to an n8n webhook on completion, which forwards the message to Telegram.
|
||||||
|
|
||||||
The webhook URL is set in two places:
|
The webhook URL is set via `WEBHOOK_URL` in the root `.env`. Both services point to the same n8n workflow at `https://n8n.pivoine.art`, which accepts `{ "message": "...", "color": "..." }` and forwards it to Telegram.
|
||||||
- `_backup/.env` → `WEBHOOK_URL`
|
|
||||||
- `watchtower/.env` → `NOTIFICATION_URL` (uses `generic+https://` shoutrrr scheme)
|
|
||||||
|
|
||||||
Both point to the same n8n workflow at `https://n8n.pivoine.art`. The workflow accepts `{ "message": "..." }` from both senders and forwards it to Telegram.
|
|
||||||
|
|
||||||
## Data
|
## Data
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
RESTIC_REPOSITORY=/mnt/hidrive/users/valknar/Backup/stacks
|
|
||||||
RESTIC_PASSWORD=change_me
|
|
||||||
WEBHOOK_URL=https://n8n.example.com/webhook/change_me
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
STACKS_DIR="$(dirname "$SCRIPT_DIR")"
|
|
||||||
DATA_DIR="$STACKS_DIR/.data"
|
|
||||||
DUMP_DIR="$DATA_DIR/backup/dumps"
|
|
||||||
LOG_FILE="$SCRIPT_DIR/backup.log"
|
|
||||||
|
|
||||||
# Load environment
|
|
||||||
set -a
|
|
||||||
source "$SCRIPT_DIR/.env"
|
|
||||||
set +a
|
|
||||||
|
|
||||||
export RESTIC_REPOSITORY RESTIC_PASSWORD
|
|
||||||
|
|
||||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
|
|
||||||
|
|
||||||
notify() {
|
|
||||||
local color="$1" text="$2"
|
|
||||||
curl -sf -o /dev/null -X POST "$WEBHOOK_URL" \
|
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{\"message\":\"$text\",\"color\":\"$color\"}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Truncate log on each run
|
|
||||||
: > "$LOG_FILE"
|
|
||||||
|
|
||||||
log "Starting backup"
|
|
||||||
|
|
||||||
# --- Postgres dumps ---
|
|
||||||
mkdir -p "$DUMP_DIR"
|
|
||||||
|
|
||||||
declare -A DATABASES=(
|
|
||||||
[umami_db]="umami:umami"
|
|
||||||
[blinko_db]="blinko:blinko"
|
|
||||||
[gitea_db]="gitea:gitea"
|
|
||||||
[n8n_db]="n8n:n8n"
|
|
||||||
[immich_db]="immich:immich"
|
|
||||||
[coolify_db]="coolify:coolify"
|
|
||||||
)
|
|
||||||
|
|
||||||
dump_errors=()
|
|
||||||
for container in "${!DATABASES[@]}"; do
|
|
||||||
IFS=: read -r user db <<< "${DATABASES[$container]}"
|
|
||||||
log "Dumping $db from $container"
|
|
||||||
if docker exec "$container" pg_dump -U "$user" "$db" > "$DUMP_DIR/$db.sql" 2>>"$LOG_FILE"; then
|
|
||||||
log " OK ($(du -h "$DUMP_DIR/$db.sql" | cut -f1))"
|
|
||||||
else
|
|
||||||
log " FAILED: $container"
|
|
||||||
dump_errors+=("$db")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ ${#dump_errors[@]} -gt 0 ]; then
|
|
||||||
log "WARNING: Failed dumps: ${dump_errors[*]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Restic backup ---
|
|
||||||
log "Running restic backup"
|
|
||||||
if ! restic backup "$DATA_DIR" --tag stacks 2>&1 | tee -a "$LOG_FILE"; then
|
|
||||||
log "FATAL: restic backup failed"
|
|
||||||
notify "#cc0000" "❌ **Backup failed** — restic backup error. Check \`$LOG_FILE\`."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Restic prune ---
|
|
||||||
log "Pruning old snapshots"
|
|
||||||
if ! restic forget --prune --keep-daily 7 --keep-weekly 4 --keep-monthly 6 2>&1 | tee -a "$LOG_FILE"; then
|
|
||||||
log "WARNING: restic forget failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Summary ---
|
|
||||||
snapshot_info=$(restic snapshots --latest 1 --compact 2>/dev/null | tail -3 | head -1)
|
|
||||||
repo_stats=$(restic stats 2>/dev/null | grep "Total Size" || true)
|
|
||||||
|
|
||||||
summary="✅ **Backup complete**"
|
|
||||||
[ ${#dump_errors[@]} -gt 0 ] && summary+="\n⚠️ Failed dumps: ${dump_errors[*]}"
|
|
||||||
summary+="\nLatest: \`$snapshot_info\`"
|
|
||||||
[ -n "$repo_stats" ] && summary+="\nRepo: $repo_stats"
|
|
||||||
|
|
||||||
notify "#36a64f" "$summary"
|
|
||||||
log "Backup complete"
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Backup stacks data to HiDrive via restic
|
|
||||||
After=network-online.target docker.service
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
User=root
|
|
||||||
ExecStart=/root/stacks/_backup/backup.sh
|
|
||||||
Environment=HOME=/root
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Daily stacks backup at 3:00 AM
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnCalendar=*-*-* 03:00:00 Europe/Amsterdam
|
|
||||||
Persistent=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
NETWORK_NAME=falcon_network
|
|
||||||
TRAEFIK_HOST=blinko.example.com
|
|
||||||
BLINKO_NEXTAUTH_SECRET=change_me
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
services:
|
|
||||||
blinko:
|
|
||||||
image: blinkospace/blinko:latest
|
|
||||||
container_name: blinko
|
|
||||||
environment:
|
|
||||||
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
|
||||||
NODE_ENV: production
|
|
||||||
NEXTAUTH_URL: https://${TRAEFIK_HOST}
|
|
||||||
NEXT_PUBLIC_BASE_URL: https://${TRAEFIK_HOST}
|
|
||||||
NEXTAUTH_SECRET: ${BLINKO_NEXTAUTH_SECRET}
|
|
||||||
DATABASE_URL: postgresql://blinko:blinko@blinko_db:5432/blinko
|
|
||||||
volumes:
|
|
||||||
- ../.data/blinko/app:/app/.blinko
|
|
||||||
depends_on:
|
|
||||||
db:
|
|
||||||
condition: service_healthy
|
|
||||||
restart: always
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.middlewares.blinko-redirect-web-secure.redirectscheme.scheme=https"
|
|
||||||
- "traefik.http.routers.blinko-web.middlewares=blinko-redirect-web-secure"
|
|
||||||
- "traefik.http.routers.blinko-web.rule=Host(`${TRAEFIK_HOST}`)"
|
|
||||||
- "traefik.http.routers.blinko-web.entrypoints=web"
|
|
||||||
- "traefik.http.routers.blinko-web-secure.rule=Host(`${TRAEFIK_HOST}`)"
|
|
||||||
- "traefik.http.routers.blinko-web-secure.tls.certresolver=resolver"
|
|
||||||
- "traefik.http.routers.blinko-web-secure.entrypoints=web-secure"
|
|
||||||
- "traefik.http.routers.blinko-web-secure.middlewares=security-headers@file,no-index@file"
|
|
||||||
- "traefik.http.services.blinko-web-secure.loadbalancer.server.port=1111"
|
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
|
||||||
- compose_network
|
|
||||||
db:
|
|
||||||
image: postgres:14-alpine
|
|
||||||
container_name: blinko_db
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: blinko
|
|
||||||
POSTGRES_USER: blinko
|
|
||||||
POSTGRES_PASSWORD: blinko
|
|
||||||
volumes:
|
|
||||||
- ../.data/blinko/db:/var/lib/postgresql/data
|
|
||||||
restart: always
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
networks:
|
|
||||||
- compose_network
|
|
||||||
networks:
|
|
||||||
compose_network:
|
|
||||||
name: ${NETWORK_NAME}
|
|
||||||
external: true
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
TRAEFIK_HOST=code.example.com
|
||||||
|
NETWORK_NAME=falcon_network
|
||||||
|
TIMEZONE=Europe/Amsterdam
|
||||||
|
DOCKER_USER=coder
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
PASSWORD=changeme
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
services:
|
||||||
|
code:
|
||||||
|
image: codercom/code-server:latest
|
||||||
|
container_name: code
|
||||||
|
user: "${PUID:-1000}:${PGID:-1000}"
|
||||||
|
environment:
|
||||||
|
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
||||||
|
DOCKER_USER: ${DOCKER_USER:-coder}
|
||||||
|
PASSWORD: ${PASSWORD}
|
||||||
|
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
|
||||||
|
volumes:
|
||||||
|
- ../.data/code:/home/coder
|
||||||
|
restart: always
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.middlewares.code-redirect-web-secure.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.code-web.middlewares=code-redirect-web-secure"
|
||||||
|
- "traefik.http.routers.code-web.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.code-web.entrypoints=web"
|
||||||
|
- "traefik.http.routers.code-web-secure.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.code-web-secure.tls.certresolver=resolver"
|
||||||
|
- "traefik.http.routers.code-web-secure.entrypoints=web-secure"
|
||||||
|
- "traefik.http.routers.code-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
|
- "traefik.http.services.code-web-secure.loadbalancer.server.port=8080"
|
||||||
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
|
networks:
|
||||||
|
- compose_network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
compose_network:
|
||||||
|
name: ${NETWORK_NAME}
|
||||||
|
external: true
|
||||||
@@ -54,7 +54,6 @@ services:
|
|||||||
- "traefik.http.routers.coolify-web-secure.priority=1"
|
- "traefik.http.routers.coolify-web-secure.priority=1"
|
||||||
- "traefik.http.services.coolify.loadbalancer.server.port=8080"
|
- "traefik.http.services.coolify.loadbalancer.server.port=8080"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
realtime:
|
realtime:
|
||||||
@@ -91,7 +90,6 @@ services:
|
|||||||
- "traefik.http.routers.coolify-terminal-ws.priority=100"
|
- "traefik.http.routers.coolify-terminal-ws.priority=100"
|
||||||
- "traefik.http.services.coolify-terminal.loadbalancer.server.port=6002"
|
- "traefik.http.services.coolify-terminal.loadbalancer.server.port=6002"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
TRAEFIK_HOST=dev.example.com
|
TRAEFIK_HOST=dev.example.com
|
||||||
NETWORK_NAME=falcon_network
|
NETWORK_NAME=falcon_network
|
||||||
RUNNER_TOKEN=change_me
|
RUNNER_TOKEN=change_me
|
||||||
|
EMAIL_FROM=gitea@pivoine.art
|
||||||
|
EMAIL_USER=Gitea
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
+3
-3
@@ -25,6 +25,8 @@ services:
|
|||||||
GITEA__mailer__PROTOCOL: smtp
|
GITEA__mailer__PROTOCOL: smtp
|
||||||
GITEA__mailer__SMTP_ADDR: mailpit
|
GITEA__mailer__SMTP_ADDR: mailpit
|
||||||
GITEA__mailer__SMTP_PORT: 1025
|
GITEA__mailer__SMTP_PORT: 1025
|
||||||
|
GITEA__mailer__FROM: ${EMAIL_FROM}
|
||||||
|
GITEA__mailer__USER: ${EMAIL_USER}
|
||||||
GITEA__service__DISABLE_REGISTRATION: "true"
|
GITEA__service__DISABLE_REGISTRATION: "true"
|
||||||
GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
|
GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
|
||||||
GITEA__service__ENABLE_NOTIFY_MAIL: "true"
|
GITEA__service__ENABLE_NOTIFY_MAIL: "true"
|
||||||
@@ -41,6 +43,7 @@ services:
|
|||||||
- ../.data/gitea/config:/etc/gitea
|
- ../.data/gitea/config:/etc/gitea
|
||||||
- ./css:/data/gitea/public/assets/css:ro
|
- ./css:/data/gitea/public/assets/css:ro
|
||||||
- ./img:/data/gitea/public/assets/img:ro
|
- ./img:/data/gitea/public/assets/img:ro
|
||||||
|
- ./templates/custom:/data/gitea/templates/custom:ro
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -58,7 +61,6 @@ services:
|
|||||||
- "traefik.http.routers.gitea-web-secure.middlewares=security-headers@file"
|
- "traefik.http.routers.gitea-web-secure.middlewares=security-headers@file"
|
||||||
- "traefik.http.services.gitea-web-secure.loadbalancer.server.port=3000"
|
- "traefik.http.services.gitea-web-secure.loadbalancer.server.port=3000"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
runner:
|
runner:
|
||||||
@@ -77,8 +79,6 @@ services:
|
|||||||
- ../.data/gitea/runner:/data
|
- ../.data/gitea/runner:/data
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- ./runner-config.yaml:/data/config.yaml:ro
|
- ./runner-config.yaml:/data/config.yaml:ro
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
restart: always
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{if and .RunModeIsProd (not .IsSigned)}}
|
||||||
|
<script defer src="https://umami.pivoine.art/script.js" data-website-id="eee7e810-116b-408a-a46d-f51dcec217c2"></script>
|
||||||
|
{{end}}
|
||||||
@@ -33,7 +33,6 @@ services:
|
|||||||
- "traefik.http.routers.immich-web-secure.middlewares=security-headers@file,no-index@file"
|
- "traefik.http.routers.immich-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
- "traefik.http.services.immich-web-secure.loadbalancer.server.port=2283"
|
- "traefik.http.services.immich-web-secure.loadbalancer.server.port=2283"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
ml:
|
ml:
|
||||||
@@ -44,8 +43,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../.data/immich/model-cache:/cache
|
- ../.data/immich/model-cache:/cache
|
||||||
restart: always
|
restart: always
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ../.data/mailpit:/data
|
- ../.data/mailpit:/data
|
||||||
restart: always
|
restart: always
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ services:
|
|||||||
- "traefik.http.routers.n8n-web-secure.middlewares=security-headers@file,no-index@file"
|
- "traefik.http.routers.n8n-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
- "traefik.http.services.n8n-web-secure.loadbalancer.server.port=5678"
|
- "traefik.http.services.n8n-web-secure.loadbalancer.server.port=5678"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
TRAEFIK_HOST=passbolt.example.com
|
||||||
|
NETWORK_NAME=falcon_network
|
||||||
|
EMAIL_FROM=passbolt@pivoine.art
|
||||||
|
EMAIL_USER=Passbolt
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
services:
|
||||||
|
passbolt:
|
||||||
|
image: passbolt/passbolt:latest-ce
|
||||||
|
container_name: passbolt
|
||||||
|
environment:
|
||||||
|
APP_FULL_BASE_URL: https://${TRAEFIK_HOST}
|
||||||
|
PASSBOLT_SSL_FORCE: "false"
|
||||||
|
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
||||||
|
PASSBOLT_REGISTRATION_PUBLIC: "false"
|
||||||
|
DATASOURCES_DEFAULT_HOST: passbolt_db
|
||||||
|
DATASOURCES_DEFAULT_PORT: "5432"
|
||||||
|
DATASOURCES_DEFAULT_DATABASE: passbolt
|
||||||
|
DATASOURCES_DEFAULT_USERNAME: passbolt
|
||||||
|
DATASOURCES_DEFAULT_PASSWORD: passbolt
|
||||||
|
DATASOURCES_DEFAULT_DRIVER: Cake\Database\Driver\Postgres
|
||||||
|
DATASOURCES_DEFAULT_ENCODING: utf8
|
||||||
|
DATASOURCES_QUOTE_IDENTIFIER: "true"
|
||||||
|
EMAIL_TRANSPORT_DEFAULT_HOST: mailpit
|
||||||
|
EMAIL_TRANSPORT_DEFAULT_PORT: "1025"
|
||||||
|
EMAIL_TRANSPORT_DEFAULT_TLS: "false"
|
||||||
|
EMAIL_DEFAULT_FROM: ${EMAIL_FROM}
|
||||||
|
EMAIL_DEFAULT_FROM_NAME: ${EMAIL_USER}
|
||||||
|
volumes:
|
||||||
|
- ../.data/passbolt/gpg:/etc/passbolt/gpg
|
||||||
|
- ../.data/passbolt/jwt:/etc/passbolt/jwt
|
||||||
|
- ../.data/passbolt/gnupg:/var/lib/passbolt/.gnupg
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.middlewares.passbolt-redirect-web-secure.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.passbolt-web.middlewares=passbolt-redirect-web-secure"
|
||||||
|
- "traefik.http.routers.passbolt-web.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.passbolt-web.entrypoints=web"
|
||||||
|
- "traefik.http.routers.passbolt-web-secure.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.passbolt-web-secure.tls.certresolver=resolver"
|
||||||
|
- "traefik.http.routers.passbolt-web-secure.entrypoints=web-secure"
|
||||||
|
- "traefik.http.routers.passbolt-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
|
- "traefik.http.services.passbolt-web-secure.loadbalancer.server.port=80"
|
||||||
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
|
networks:
|
||||||
|
- compose_network
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: passbolt_db
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: passbolt
|
||||||
|
POSTGRES_USER: passbolt
|
||||||
|
POSTGRES_PASSWORD: passbolt
|
||||||
|
POSTGRES_INITDB_ARGS: --data-checksums
|
||||||
|
volumes:
|
||||||
|
- ../.data/passbolt/db:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- compose_network
|
||||||
|
networks:
|
||||||
|
compose_network:
|
||||||
|
name: ${NETWORK_NAME}
|
||||||
|
external: true
|
||||||
@@ -28,7 +28,6 @@ services:
|
|||||||
- "traefik.http.routers.umami-web-secure.middlewares=security-headers@file,no-index@file"
|
- "traefik.http.routers.umami-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
- "traefik.http.services.umami-web-secure.loadbalancer.server.port=3000"
|
- "traefik.http.services.umami-web-secure.loadbalancer.server.port=3000"
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- compose_network
|
- compose_network
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
TRAEFIK_HOST=vault.example.com
|
|
||||||
NETWORK_NAME=falcon_network
|
|
||||||
SMTP_FROM=vaultwarden@example.com
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
services:
|
|
||||||
vaultwarden:
|
|
||||||
image: vaultwarden/server:latest
|
|
||||||
container_name: vaultwarden
|
|
||||||
environment:
|
|
||||||
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
|
||||||
DOMAIN: https://${TRAEFIK_HOST}
|
|
||||||
WEBSOCKET_ENABLED: "true"
|
|
||||||
SIGNUPS_ALLOWED: "true"
|
|
||||||
INVITATIONS_ALLOWED: "true"
|
|
||||||
SHOW_PASSWORD_HINT: "false"
|
|
||||||
SMTP_HOST: mailpit
|
|
||||||
SMTP_FROM: ${SMTP_FROM}
|
|
||||||
SMTP_FROM_NAME: Vaultwarden
|
|
||||||
SMTP_SECURITY: off
|
|
||||||
SMTP_PORT: 1025
|
|
||||||
volumes:
|
|
||||||
- ../.data/vaultwarden:/data
|
|
||||||
restart: always
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.middlewares.vaultwarden-redirect-web-secure.redirectscheme.scheme=https"
|
|
||||||
- "traefik.http.routers.vaultwarden-web.middlewares=vaultwarden-redirect-web-secure"
|
|
||||||
- "traefik.http.routers.vaultwarden-web.rule=Host(`${TRAEFIK_HOST}`)"
|
|
||||||
- "traefik.http.routers.vaultwarden-web.entrypoints=web"
|
|
||||||
- "traefik.http.routers.vaultwarden-web-secure.rule=Host(`${TRAEFIK_HOST}`)"
|
|
||||||
- "traefik.http.routers.vaultwarden-web-secure.tls.certresolver=resolver"
|
|
||||||
- "traefik.http.routers.vaultwarden-web-secure.entrypoints=web-secure"
|
|
||||||
- "traefik.http.routers.vaultwarden-web-secure.middlewares=security-headers@file,no-index@file"
|
|
||||||
- "traefik.http.services.vaultwarden-web-secure.loadbalancer.server.port=80"
|
|
||||||
- "traefik.docker.network=${NETWORK_NAME}"
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
|
||||||
- compose_network
|
|
||||||
networks:
|
|
||||||
compose_network:
|
|
||||||
name: ${NETWORK_NAME}
|
|
||||||
external: true
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
NOTIFICATION_URL=generic+https://n8n.example.com/webhook/change_me
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
services:
|
|
||||||
watchtower:
|
|
||||||
image: containrrr/watchtower:latest
|
|
||||||
container_name: watchtower
|
|
||||||
environment:
|
|
||||||
DOCKER_API_VERSION: "1.44"
|
|
||||||
WATCHTOWER_POLL_INTERVAL: 300
|
|
||||||
WATCHTOWER_LABEL_ENABLE: "true"
|
|
||||||
WATCHTOWER_CLEANUP: "true"
|
|
||||||
WATCHTOWER_INCLUDE_STOPPED: "false"
|
|
||||||
WATCHTOWER_INCLUDE_RESTARTING: "true"
|
|
||||||
WATCHTOWER_RUN_ONCE: "false"
|
|
||||||
WATCHTOWER_NOTIFICATIONS: ${NOTIFICATIONS:-}
|
|
||||||
WATCHTOWER_NOTIFICATION_URL: ${NOTIFICATION_URL:-}
|
|
||||||
WATCHTOWER_LOG_LEVEL: info
|
|
||||||
WATCHTOWER_ROLLING_RESTART: "false"
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
restart: always
|
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
Reference in New Issue
Block a user