Compare commits

...
13 changed files with 2351 additions and 13 deletions
+38
View File
@@ -58,6 +58,7 @@ Edit the generated `.env` files to fill in the required fields:
- `./stalwart/.env`
- `./caddy/.env`
- `./crowdsec/.env`
- `./prometheus/.env`
- `./caddy/Caddyfile.private`
### 4. Bouncer Keys (CrowdSec)
@@ -114,6 +115,43 @@ docker exec crowdsec cscli bouncers list # 'firewall' should appear with a
sudo nft list ruleset | grep crowdsec # kernel-level rules in place
```
## Backups (optional, restic)
This is **optional** — use it only if you want off-site, encrypted backups of your service data. Here it is done with [restic](https://github.com/restic/restic) over SFTP (e.g. a Hetzner Storage Box). restic encrypts client-side, deduplicates and compresses, so no separate `tar`/`gzip` is needed. Run as **root**, since Docker-created service data is mostly root-owned.
Replace the placeholders: `USER@HOST:PORT` (SFTP target), `/path/to/restic-repo` (repo path on the server), `YOUR_PASSWORD` (losing it means the backup is unrecoverable), `/path/to/services` (directory to back up). Do not commit real secrets to git.
```sh
# 1. Install restic and set up passwordless SSH to the target (as root)
sudo apt install restic
sudo -i
ssh-keygen -t ed25519 && ssh-copy-id -p PORT USER@HOST # no passphrase
# 2. Backup script: /usr/local/bin/restic-backup.sh (chmod 700)
#!/bin/bash
export RESTIC_REPOSITORY="sftp://USER@HOST:PORT//path/to/restic-repo" # note the double slash before an absolute path
export RESTIC_PASSWORD="YOUR_PASSWORD"
restic backup /path/to/services --compression max
restic forget --keep-last 3 --prune # keep only the 3 newest snapshots
# 3. Initialize once, then test
restic init
/usr/local/bin/restic-backup.sh && restic snapshots
# 4. Schedule via cron (3x/day at 06:00, 14:00, 22:00)
sudo crontab -e
# 0 6,14,22 * * * /usr/local/bin/restic-backup.sh >> /var/log/restic-backup.log 2>&1
```
Restore (from any machine with restic + repo access + password, as root):
```sh
restic snapshots
restic restore latest --target /restore/destination
```
Other useful commands: `restic check` (verify integrity), `restic unlock` (clear a stale lock), `restic stats`.
## Stopping Services
To stop all running services, use:
+2 -2
View File
@@ -128,7 +128,7 @@
max_size 8120MB
}
route {
import security
crowdsec
reverse_proxy http://sftpgo:8080 {
header_up Host {http.request.host}
}
@@ -170,7 +170,7 @@
max_size 1024MB
}
route {
import security
crowdsec
reverse_proxy http://immich_server:2283 {
header_up Host {http.request.host}
}
+2 -1
View File
@@ -1,5 +1,6 @@
source: journalctl
journalctl_filter:
- "_SYSTEMD_UNIT=ssh.service"
# NixOS names the OpenSSH unit "sshd.service"; Debian/Ubuntu use "ssh.service".
- "_SYSTEMD_UNIT=sshd.service"
labels:
type: syslog
+16 -1
View File
@@ -48,6 +48,11 @@ services:
- /run/log/journal:/run/log/journal:ro
- /etc/machine-id:/etc/machine-id:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
deploy:
resources:
limits:
cpus: "0.6"
memory: 768M
logging:
driver: "json-file"
options:
@@ -63,13 +68,18 @@ services:
environment:
CROWDSEC_LAPI_URL: "http://crowdsec:8080"
CROWDSEC_API_KEY: "${CROWDSEC_BOUNCER_KEY_EXPORTER}"
POLL_INTERVAL_SECS: "30"
POLL_INTERVAL_SECS: "300"
LISTEN_PORT: "9100"
GEOIP_CITY_DB: "/geoip/GeoLite2-City.mmdb"
volumes:
- ./data/geoip:/geoip:ro
depends_on:
- crowdsec
deploy:
resources:
limits:
cpus: "0.25"
memory: 128M
logging:
driver: "json-file"
options:
@@ -89,6 +99,11 @@ services:
GEOIPUPDATE_FREQUENCY: "24"
volumes:
- ./data/geoip:/usr/share/GeoIP
deploy:
resources:
limits:
cpus: "0.25"
memory: 64M
logging:
driver: "json-file"
options:
+1 -1
View File
@@ -6,7 +6,7 @@ networks:
services:
glance:
image: glanceapp/glance:v0.8.4
image: glanceapp/glance:v0.8.5
container_name: glance
restart: unless-stopped
networks:
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -9,7 +9,7 @@ networks:
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:v2
image: ghcr.io/immich-app/immich-server:v3
labels:
- "com.centurylinklabs.watchtower.enable=true"
volumes:
@@ -65,7 +65,7 @@ services:
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:8-bookworm@sha256:facc1d2c3462975c34e10fccb167bfa92b0e0dbd992fc282c29a61c3243afb11
image: docker.io/valkey/valkey:9@sha256:4963247afc4cd33c7d3b2d2816b9f7f8eeebab148d29056c2ca4d7cbc966f2d9
networks:
- immich
healthcheck:
@@ -79,7 +79,7 @@ services:
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
+1
View File
@@ -65,6 +65,7 @@ generate_env_files() {
cp --update=none ./croc/.env.example ./croc/.env
cp --update=none ./stalwart/.env.example ./stalwart/.env
cp --update=none ./crowdsec/.env.example ./crowdsec/.env
cp --update=none ./prometheus/.env.example ./prometheus/.env
cp --update=none ./caddy/Caddyfile.private.example ./caddy/Caddyfile.private
print_success ".env files generated."
}
+4
View File
@@ -0,0 +1,4 @@
############# Hetzner Storage Box exporter #############
# Read-only Hetzner Cloud API token: Cloud Console -> Security -> API Tokens.
# The Storage Box must be managed via the Hetzner Cloud API.
HETZNER_TOKEN=
+5
View File
@@ -26,3 +26,8 @@ scrape_configs:
scrape_interval: 30s
static_configs:
- targets: ["crowdsec_exporter:9100"]
- job_name: storagebox_exporter
scrape_interval: 60s
static_configs:
- targets: ["storagebox_exporter:9509"]
+19
View File
@@ -72,5 +72,24 @@ services:
max-size: "100m"
max-file: "3"
storagebox_exporter:
image: ghcr.io/crstian19/prometheus-storagebox-exporter:0.5
container_name: storagebox_exporter
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=true"
networks:
- prometheus
environment:
HETZNER_TOKEN: "${HETZNER_TOKEN}"
LISTEN_ADDRESS: ":9509"
METRICS_PATH: "/metrics"
LOG_LEVEL: "info"
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
volumes:
prometheus-data:
+2 -2
View File
@@ -1,6 +1,6 @@
# https://docs.sftpgo.com/latest/config-file/ (Common > idle_timeout)
SFTPGO_COMMON_IDLE_TIMEOUT=15
SFTPGO_COMMON__IDLE_TIMEOUT=120
# https://docs.sftpgo.com/latest/config-file/ (Common > upload_mode)
SFTPGO_COMMON_UPLOAD_MODE=0
SFTPGO_COMMON__UPLOAD_MODE=0
# https://docs.sftpgo.com/latest/config-file/ (HTTP server > bindings > ip_proxy_header)
SFTPGO_HTTPD__BINDINGS__0__CLIENT_IP_PROXY_HEADER=X-Real-IP
+2 -3
View File
@@ -14,10 +14,9 @@ services:
labels:
- "com.centurylinklabs.watchtower.enable=true"
environment:
- SFTPGO_COMMON_IDLE_TIMEOUT=${SFTPGO_COMMON_IDLE_TIMEOUT}
- SFTPGO_COMMON_UPLOAD_MODE=${SFTPGO_COMMON_UPLOAD_MODE}
- SFTPGO_COMMON__IDLE_TIMEOUT=${SFTPGO_COMMON__IDLE_TIMEOUT}
- SFTPGO_COMMON__UPLOAD_MODE=${SFTPGO_COMMON__UPLOAD_MODE}
- SFTPGO_HTTPD__BINDINGS__0__CLIENT_IP_PROXY_HEADER=${SFTPGO_HTTPD__BINDINGS__0__CLIENT_IP_PROXY_HEADER}
- SFTPGO_SFTPD__KEEP_ALIVE_INTERVAL=30
networks:
- caddy
volumes: