Compare commits
45 Commits
0c9048e351
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 35c8657f13 | |||
| c6436d720e | |||
| 8e38610b28 | |||
| b1cd0e1050 | |||
| 46f8b5e0a5 | |||
| ea33c80bb6 | |||
| 793e11699d | |||
| 8057b502ef | |||
| 994703974d | |||
| bed8333d37 | |||
| 50eabae5ab | |||
| a2928c888f | |||
| dc0d3801bd | |||
| 9f3339b6da | |||
| 27d61f8ee7 | |||
| be46801df6 | |||
| 65cff068d6 | |||
| f47da92f3c | |||
| c3c305ac54 | |||
| 11ec28dfcd | |||
| adc590f346 | |||
| eea96b9f02 | |||
| 5b0e798d37 | |||
| ac8364a11a | |||
| caf2af8c60 | |||
| 3065a31d08 | |||
| e95a2d9bbc | |||
| 3e72b8d544 | |||
| a14978c410 | |||
| 746ebe0a59 | |||
| bc544a9b49 | |||
| 8e50aaa5d2 | |||
| 22529c4113 | |||
| 32e62ee787 | |||
| e733ba3ae1 | |||
| 927beb1366 | |||
| c1db89fe6f | |||
| 26c4d2b1b7 | |||
| 229897a3d6 | |||
| 05dad9ccf7 | |||
| 08c79ee0eb | |||
| 25146ec428 | |||
| 834a4b97d2 | |||
| 6ffd44d11f | |||
| cd1b746dcc |
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
.secrets
|
||||
.env
|
||||
|
||||
|
||||
|
||||
webapp/nginx-test
|
||||
webapp/dad-domains
|
||||
webapp/qbittorrent
|
||||
webapp/openwebui
|
||||
webapp/wisemapping
|
||||
remote/rustdesk-server
|
||||
remote/ttyd-ubuntu
|
||||
mgmt/grafana
|
||||
mgmt/prometheus
|
||||
local/Y2JB-WebUI
|
||||
67
.vscode/settings.json
vendored
Normal file
67
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"yaml.schemas": {
|
||||
"https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json": [
|
||||
"**/docker-compose*.yml",
|
||||
"**/docker-compose*.yaml"
|
||||
],
|
||||
"https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.29.0/all.json": [
|
||||
"**/*.k8s.yaml",
|
||||
"**/*.k8s.yml",
|
||||
"**/deployment*.yaml",
|
||||
"**/deployment*.yml",
|
||||
"**/service*.yaml",
|
||||
"**/service*.yml",
|
||||
"**/ingress*.yaml",
|
||||
"**/ingress*.yml",
|
||||
"**/configmap*.yaml",
|
||||
"**/configmap*.yml",
|
||||
"**/secret*.yaml",
|
||||
"**/secret*.yml",
|
||||
"**/pod*.yaml",
|
||||
"**/pod*.yml",
|
||||
"**/namespace*.yaml",
|
||||
"**/namespace*.yml",
|
||||
"**/replicaset*.yaml",
|
||||
"**/replicaset*.yml",
|
||||
"**/statefulset*.yaml",
|
||||
"**/statefulset*.yml",
|
||||
"**/daemonset*.yaml",
|
||||
"**/daemonset*.yml",
|
||||
"**/job*.yaml",
|
||||
"**/job*.yml",
|
||||
"**/cronjob*.yaml",
|
||||
"**/cronjob*.yml",
|
||||
"**/role*.yaml",
|
||||
"**/role*.yml",
|
||||
"**/rolebinding*.yaml",
|
||||
"**/rolebinding*.yml",
|
||||
"**/clusterrole*.yaml",
|
||||
"**/clusterrole*.yml",
|
||||
"**/clusterrolebinding*.yaml",
|
||||
"**/clusterrolebinding*.yml",
|
||||
"**/networkpolicy*.yaml",
|
||||
"**/networkpolicy*.yml",
|
||||
"**/persistentvolume*.yaml",
|
||||
"**/persistentvolume*.yml",
|
||||
"**/persistentvolumeclaim*.yaml",
|
||||
"**/persistentvolumeclaim*.yml",
|
||||
"**/storageclass*.yaml",
|
||||
"**/storageclass*.yml",
|
||||
"**/kustomization*.yaml",
|
||||
"**/kustomization*.yml"
|
||||
],
|
||||
"https://json.schemastore.org/chart.json": [
|
||||
"**/Chart.yaml",
|
||||
"**/Chart.yml"
|
||||
],
|
||||
"https://json.schemastore.org/github-workflow.json": [
|
||||
"**/.github/workflows/*.yml",
|
||||
"**/.github/workflows/*.yaml"
|
||||
]
|
||||
},
|
||||
"yaml.validate": true,
|
||||
"yaml.format.enable": true,
|
||||
"yaml.hover": true,
|
||||
"yaml.completion": true,
|
||||
"yaml.suggest.parentSkeletonSelectedFirst": true
|
||||
}
|
||||
147
AGENTS.md
Normal file
147
AGENTS.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Agent Guidelines for Docker Infrastructure Monorepo
|
||||
|
||||
## CRITICAL: Forbidden Files
|
||||
|
||||
**NEVER read, access, or attempt to open the following files under ANY circumstances:**
|
||||
|
||||
- `.env` and ALL variants (`.env.local`, `.env.development`, `.env.production`, `.env.*`)
|
||||
- `.secrets/` directories at any level
|
||||
- Any file in a `secrets/` directory at any level
|
||||
- Credential files: `credentials.json`, `credentials.yml`, `credentials.yaml`
|
||||
- Key files: `*.pem`, `*.key`, `*.p12`, `*.pfx`, `id_rsa`, `id_ecdsa`, `id_ed25519`
|
||||
- Files named `token`, `tokens`, `.token`, `.tokens`, `password`, `passwords`
|
||||
|
||||
**What to do instead:**
|
||||
- Use `.env.example` files to understand required environment variables
|
||||
- Ask the user to provide values explicitly if needed
|
||||
- Use placeholder values when demonstrating code
|
||||
|
||||
This is a hard rule. No exceptions. No "just checking". If you need env info, read the `.env.example` file or ask the user.
|
||||
|
||||
## Repository Structure
|
||||
|
||||
This is a Docker Compose monorepo for personal infrastructure hosting. The main focus is Docker services, with local tools in `local/`.
|
||||
|
||||
### Core Infrastructure
|
||||
- `frontend/` - Cloudflare Tunnel (cloudflared/) and Traefik reverse proxy (traefik/)
|
||||
- `backend/` - Core data services (postgres/, redis/)
|
||||
- `mgmt/` - Management tools (portainer/, gitea/, grafana/, prometheus/, vaultwarden/, nextcloud/, authentik/)
|
||||
- `webapp/` - Applications (n8n/, navidrome/, it-tools/, openwebui/, qbittorrent/, super-productivity/)
|
||||
- `templates/` - Configuration templates
|
||||
- `remote/` - Remote access utilities
|
||||
|
||||
### Local Tools
|
||||
- `local/openclaw/` - WhatsApp gateway CLI (Node.js project, see its own AGENTS.md)
|
||||
|
||||
## Docker Commands
|
||||
|
||||
### Managing Services
|
||||
|
||||
```bash
|
||||
# Start a service
|
||||
cd <service-path> && docker compose up -d
|
||||
|
||||
# Stop a service
|
||||
cd <service-path> && docker compose down
|
||||
|
||||
# View logs
|
||||
cd <service-path> && docker compose logs -f
|
||||
|
||||
# Restart a service
|
||||
cd <service-path> && docker compose restart
|
||||
|
||||
# Update/pull latest images
|
||||
cd <service-path> && docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
### Network Architecture
|
||||
|
||||
- **Traefik** (frontend/traefik/) - Reverse proxy and load balancer
|
||||
- **Cloudflared** (frontend/cloudflared/) - Cloudflare Tunnel for secure external access
|
||||
- Services use Docker networks defined in their compose.yml files
|
||||
- No port forwarding required on home router
|
||||
|
||||
## Service Configuration
|
||||
|
||||
### Environment Variables
|
||||
- Copy `.env.example` to `.env` and fill in values
|
||||
- Never commit `.env` files (gitignored)
|
||||
- Use `.secrets/` directory for sensitive files (gitignored)
|
||||
|
||||
### Traefik Routing
|
||||
Services exposed via Traefik use labels in compose.yml:
|
||||
- `traefik.enable=true`
|
||||
- `traefik.http.routers.<name>.rule=Host(`service.domain.com`)`
|
||||
|
||||
### Volume Mounts
|
||||
- Data volumes mounted to local directories (e.g., `./data`, `./config`)
|
||||
- Some services use named Docker volumes
|
||||
- Backup important data directories regularly
|
||||
|
||||
## Working with OpenClaw (local/openclaw/)
|
||||
|
||||
This is a TypeScript Node.js 22+ project with its own tooling. If you need to work on it:
|
||||
|
||||
```bash
|
||||
cd local/openclaw/
|
||||
|
||||
# Install and build
|
||||
pnpm install
|
||||
pnpm build
|
||||
|
||||
# Test commands
|
||||
pnpm test # Run all tests
|
||||
pnpm test -- src/path/file.test.ts # Run single test
|
||||
pnpm test:coverage # Run with coverage
|
||||
pnpm test:e2e # Run e2e tests
|
||||
|
||||
# Lint and format
|
||||
pnpm lint
|
||||
pnpm lint:fix
|
||||
pnpm format
|
||||
pnpm format:fix
|
||||
|
||||
# Pre-commit: pnpm lint && pnpm build && pnpm test
|
||||
```
|
||||
|
||||
For full OpenClaw guidelines, see `local/openclaw/AGENTS.md`.
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### Docker Compose
|
||||
- Use `compose.yml` (not docker-compose.yml)
|
||||
- Keep services focused and minimal
|
||||
- Use environment files for configuration
|
||||
- Mount volumes for persistent data
|
||||
|
||||
### Scripts
|
||||
- Shell scripts in `scripts/` directories
|
||||
- Use `#!/bin/bash` or `#!/bin/sh` shebang
|
||||
- Make scripts executable with `chmod +x`
|
||||
|
||||
### Python (local tools)
|
||||
- Type hints preferred
|
||||
- Use `pyproject.toml` for configuration
|
||||
- Virtual environments for dependencies
|
||||
|
||||
### TypeScript (OpenClaw only)
|
||||
- ESM modules (`"type": "module"`)
|
||||
- Use `node:` prefix for Node.js built-ins
|
||||
- Add `.js` extension to imports (e.g., `from "./file.js"`)
|
||||
- PascalCase for types, camelCase for functions
|
||||
- Run `pnpm lint` before commits
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- See **CRITICAL: Forbidden Files** section above — this is the highest priority rule in this repo
|
||||
- Never commit credentials, API keys, or certificates
|
||||
- Use Cloudflare Tunnel for external access (no port forwarding)
|
||||
- Placeholders in documentation (e.g., `user@example.com`)
|
||||
- Credentials stored outside repo in standard locations
|
||||
|
||||
## Git Conventions
|
||||
|
||||
- Conventional commit messages (e.g., `service: add health check`)
|
||||
- Group related changes by service
|
||||
- Don't mix unrelated service changes in one commit
|
||||
- Test Docker services start correctly after changes
|
||||
3
backend/postgres/.gitignore
vendored
Normal file
3
backend/postgres/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
.secrets/
|
||||
init-data.sh
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
start_period: 20s
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- pg_isready -U $${POSTGRES_USER_FILE}
|
||||
- pg_isready -U "$$(cat /run/secrets/POSTGRES_USER)"
|
||||
timeout: 5s
|
||||
env_file:
|
||||
- .env
|
||||
@@ -19,7 +19,7 @@ services:
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql
|
||||
networks:
|
||||
- db
|
||||
- backend
|
||||
volumes:
|
||||
pgdata:
|
||||
name: pgdata
|
||||
@@ -29,6 +29,6 @@ secrets:
|
||||
POSTGRES_PASS:
|
||||
file: .secrets/POSTGRES_PASS
|
||||
networks:
|
||||
db:
|
||||
backend:
|
||||
external:
|
||||
true
|
||||
107
backend/postgres/guides/WAL_RECOVERY.md
Normal file
107
backend/postgres/guides/WAL_RECOVERY.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# PostgreSQL WAL Corruption Recovery Guide
|
||||
|
||||
## Symptoms
|
||||
|
||||
PostgreSQL container crashes on startup with logs showing:
|
||||
|
||||
```
|
||||
LOG: unexpected pageaddr X/Y in WAL segment ...
|
||||
LOG: invalid checkpoint record
|
||||
PANIC: could not locate a valid checkpoint record at ...
|
||||
LOG: startup process (PID N) was terminated by signal 6: Aborted
|
||||
LOG: aborting startup due to startup process failure
|
||||
```
|
||||
|
||||
The container restarts repeatedly, each time hitting the same error.
|
||||
|
||||
## Cause
|
||||
|
||||
The Write-Ahead Log (WAL) was corrupted by an unclean shutdown (power loss, host crash, force kill, etc.). PostgreSQL cannot find a valid checkpoint to resume from.
|
||||
|
||||
## What You Risk Losing
|
||||
|
||||
- **Committed data**: Safe. It is already written to the data files.
|
||||
- **Uncommitted transactions** from the moment of the crash: Lost. These were only in WAL.
|
||||
- **Recent changes** that were committed but not yet checkpointed: Usually safe, but there is a small risk of inconsistency.
|
||||
|
||||
## Recovery Procedure
|
||||
|
||||
### 1. Stop the Crashing Container
|
||||
|
||||
```bash
|
||||
cd /path/to/postgres/service
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### 2. Run `pg_resetwal`
|
||||
|
||||
This resets the WAL and forces a clean start.
|
||||
|
||||
**If your data is in a named Docker volume (e.g., `pgdata`):**
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v pgdata:/var/lib/postgresql \
|
||||
--user postgres \
|
||||
postgres:18 \
|
||||
pg_resetwal -f /var/lib/postgresql/18/docker
|
||||
```
|
||||
|
||||
> Adjust the path `/var/lib/postgresql/18/docker` to match your `PGDATA` setting.
|
||||
|
||||
**If your data is in a bind mount (e.g., `./data`):**
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v $(pwd)/data:/var/lib/postgresql/data \
|
||||
--user postgres \
|
||||
postgres:18 \
|
||||
pg_resetwal -f /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
### 3. Start the Database
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 4. Verify
|
||||
|
||||
```bash
|
||||
docker compose logs --tail=20
|
||||
docker inspect --format='{{.State.Health.Status}}' postgres
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```
|
||||
LOG: database system is ready to accept connections
|
||||
```
|
||||
|
||||
And the health status should be `healthy`.
|
||||
|
||||
## Prevention
|
||||
|
||||
- Ensure graceful shutdowns: `docker compose down` instead of `docker kill`
|
||||
- Use a UPS if running on bare metal to avoid power-loss crashes
|
||||
- Keep backups of important data volumes
|
||||
- Consider setting `restart: unless-stopped` instead of `always` to prevent rapid crash loops
|
||||
|
||||
## When NOT to Use This Fix
|
||||
|
||||
Do **not** use `pg_resetwal` if:
|
||||
- You have a recent base backup and WAL archive — restore from backup instead
|
||||
- You suspect data file corruption (not just WAL corruption)
|
||||
- You can recover by other means (e.g., starting from a replication standby)
|
||||
|
||||
If unsure, copy the data directory somewhere safe before running `pg_resetwal`.
|
||||
|
||||
## One-Liner for Future Emergencies
|
||||
|
||||
If you're sure it's WAL corruption and you know your setup:
|
||||
|
||||
```bash
|
||||
docker compose down && \
|
||||
docker run --rm -v pgdata:/var/lib/postgresql --user postgres postgres:18 pg_resetwal -f /var/lib/postgresql/18/docker && \
|
||||
docker compose up -d
|
||||
```
|
||||
@@ -15,12 +15,12 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
- db
|
||||
- backend
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
volumes:
|
||||
redis_data:
|
||||
name: redis_data
|
||||
networks:
|
||||
db:
|
||||
backend:
|
||||
external: true
|
||||
4
backend/surrealdb/.env.example
Normal file
4
backend/surrealdb/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
# SurrealDB Configuration
|
||||
# Root password is loaded from ./.secrets/SURREAL_ROOT_PASSWORD via Docker secrets
|
||||
# Timezone settings
|
||||
TZ=
|
||||
28
backend/surrealdb/compose.yml
Normal file
28
backend/surrealdb/compose.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
services:
|
||||
surrealdb:
|
||||
image: surrealdb/surrealdb:v2
|
||||
container_name: surrealdb
|
||||
# entrypoint: ["/bin/sh"]
|
||||
command: start --user root --pass $(cat /run/secrets/SURREAL_ROOT_PASSWORD) rocksdb:/mydata/mydatabase.db
|
||||
user: root
|
||||
restart: always
|
||||
env_file:
|
||||
- .env
|
||||
secrets:
|
||||
- SURREAL_ROOT_PASSWORD
|
||||
volumes:
|
||||
- surreal_data:/mydata
|
||||
networks:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
surreal_data:
|
||||
name: surreal_data
|
||||
|
||||
secrets:
|
||||
SURREAL_ROOT_PASSWORD:
|
||||
file: ./.secrets/SURREAL_ROOT_PASSWORD
|
||||
|
||||
networks:
|
||||
db:
|
||||
external: true
|
||||
3
frontend/cloudflared/.gitignore
vendored
Normal file
3
frontend/cloudflared/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
tunnel.json
|
||||
config.yml
|
||||
@@ -8,9 +8,11 @@ services:
|
||||
- traefik.enable=true
|
||||
env_file:
|
||||
- .env
|
||||
expose:
|
||||
- "7844"
|
||||
networks:
|
||||
- frontend
|
||||
networks:
|
||||
frontend:
|
||||
external:
|
||||
true
|
||||
true
|
||||
|
||||
@@ -3,6 +3,8 @@ SUBDOMAIN=
|
||||
# TRAEFIK_USER=
|
||||
SSL_EMAIL_FILE=/run/secrets/CF_API_EMAIL
|
||||
CF_API_EMAIL_FILE=/run/secrets/CF_API_EMAIL
|
||||
CF_API_KEY_FILE=/run/secrets/CF_API_KEY
|
||||
ACME_EMAIL=
|
||||
CF_DNS_API_TOKEN_FILE=/run/secrets/CF_DNS_API_TOKEN
|
||||
SSH_PORT=
|
||||
TZ=
|
||||
TZ=
|
||||
TAG=
|
||||
2
frontend/traefik/.gitignore
vendored
Normal file
2
frontend/traefik/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.env
|
||||
traefik_data/
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v3.5.3"
|
||||
image: "traefik:${TAG}"
|
||||
container_name: ${SUBDOMAIN}
|
||||
restart: always
|
||||
command:
|
||||
@@ -20,13 +20,12 @@ services:
|
||||
- "--entrypoints.websecure.http.tls.certresolver=cloudflare"
|
||||
- "--entrypoints.websecure.http.tls.domains[0].main=${DOMAIN_NAME}"
|
||||
- "--entrypoints.websecure.http.tls.domains[0].sans=*.${DOMAIN_NAME}"
|
||||
- "--entrypoints.ssh.address=:${SSH_PORT}"
|
||||
# Cloudflare IPs trusted for forwarded headers
|
||||
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||
- "--certificatesresolvers.cloudflare.acme.dnschallenge=true"
|
||||
- "--certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare"
|
||||
- "--certificatesresolvers.cloudflare.acme.email=${CF_API_EMAIL_FILE}"
|
||||
- "--certificatesresolvers.cloudflare.acme.email=${ACME_EMAIL}"
|
||||
- "--certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json"
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
@@ -49,31 +48,29 @@ services:
|
||||
- traefik.http.middlewares.traefik_dashboard.headers.STSIncludeSubdomains=true
|
||||
- traefik.http.middlewares.traefik_dashboard.headers.STSPreload=true
|
||||
- traefik.http.middlewares.traefik_dashboard.headers.frameDeny=true
|
||||
- traefik.docker.network=mgmt
|
||||
env_file:
|
||||
- .env
|
||||
secrets:
|
||||
- SSH_PORT
|
||||
- CF_API_KEY
|
||||
- CF_API_EMAIL
|
||||
- CF_DNS_API_TOKEN
|
||||
volumes:
|
||||
- ./traefik_data:/letsencrypt
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "558:558"
|
||||
networks:
|
||||
- frontend
|
||||
- webapp
|
||||
- mgmt
|
||||
- remote
|
||||
- jump
|
||||
- mcp
|
||||
secrets:
|
||||
SSH_PORT:
|
||||
file: .secrets/SSH_PORT
|
||||
CF_API_KEY:
|
||||
file: .secrets/CF_API_KEY
|
||||
CF_API_EMAIL:
|
||||
file: .secrets/CF_API_EMAIL
|
||||
CF_DNS_API_TOKEN:
|
||||
file: .secrets/CF_DNS_API_TOKEN
|
||||
networks:
|
||||
frontend:
|
||||
external:
|
||||
@@ -84,6 +81,9 @@ networks:
|
||||
mgmt:
|
||||
external:
|
||||
true
|
||||
remote:
|
||||
jump:
|
||||
external:
|
||||
true
|
||||
true
|
||||
mcp:
|
||||
external:
|
||||
true
|
||||
|
||||
3
local/adguard/.gitignore
vendored
Normal file
3
local/adguard/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
conf/
|
||||
work/
|
||||
1
mgmt/adminer/.gitignore
vendored
Normal file
1
mgmt/adminer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
adminer:
|
||||
image: adminer:5.4.0
|
||||
image: adminer:${TAG}
|
||||
container_name: $SUBDOMAIN
|
||||
restart: always
|
||||
labels:
|
||||
@@ -25,11 +25,11 @@ services:
|
||||
- .env
|
||||
networks:
|
||||
- mgmt
|
||||
- db
|
||||
- backend
|
||||
networks:
|
||||
mgmt:
|
||||
external:
|
||||
true
|
||||
db:
|
||||
backend:
|
||||
external:
|
||||
true
|
||||
3
mgmt/authentik/.gitignore
vendored
Normal file
3
mgmt/authentik/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
certs/
|
||||
media/
|
||||
custom-templates/
|
||||
@@ -13,7 +13,7 @@ services:
|
||||
- ./custom-templates:/templates
|
||||
networks:
|
||||
- mgmt
|
||||
- db
|
||||
- backend
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.$SUBDOMAIN.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
- DB_PASS
|
||||
user: root
|
||||
networks:
|
||||
- db
|
||||
- backend
|
||||
volumes:
|
||||
# - /var/run/docker.sock:/var/run/docker.sock # Optional, only if using external outposts
|
||||
- ./media:/media
|
||||
@@ -64,7 +64,7 @@ services:
|
||||
networks:
|
||||
mgmt:
|
||||
external: true
|
||||
db:
|
||||
backend:
|
||||
external: true
|
||||
secrets:
|
||||
SECRET_KEY:
|
||||
|
||||
@@ -38,14 +38,14 @@ services:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
networks:
|
||||
- mgmt
|
||||
- db
|
||||
- backend
|
||||
volumes:
|
||||
gitea-data:
|
||||
name: gitea-data
|
||||
networks:
|
||||
mgmt:
|
||||
external: true
|
||||
db:
|
||||
backend:
|
||||
external: true
|
||||
secrets:
|
||||
DB_PASS:
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
DOMAIN_NAME=
|
||||
SUBDOMAIN=
|
||||
SUBDOMAIN=
|
||||
TAG=
|
||||
2
mgmt/portainer/.gitignore
vendored
Normal file
2
mgmt/portainer/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
data/
|
||||
.env
|
||||
@@ -1,12 +1,12 @@
|
||||
services:
|
||||
portainer:
|
||||
image: portainer/portainer-ce:lts
|
||||
image: portainer/portainer-ce:${TAG:-latest}
|
||||
container_name: portainer
|
||||
restart: always
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.portainer.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
- traefik.docker.network=webapp
|
||||
- traefik.docker.network=mgmt
|
||||
- traefik.http.services.portainer.loadbalancer.server.port=9000
|
||||
- traefik.http.routers.portainer.tls=true
|
||||
- traefik.http.routers.portainer.entrypoints=web,websecure
|
||||
@@ -20,9 +20,13 @@ services:
|
||||
- traefik.http.middlewares.portainer.headers.STSIncludeSubdomains=true
|
||||
- traefik.http.middlewares.portainer.headers.STSPreload=true
|
||||
- traefik.http.routers.portainer.middlewares=portainer@docker
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./data:/data
|
||||
expose:
|
||||
- 9000
|
||||
networks:
|
||||
- mgmt
|
||||
networks:
|
||||
|
||||
2
mgmt/vaultwarden/.gitignore
vendored
Normal file
2
mgmt/vaultwarden/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.env
|
||||
.recovery_code
|
||||
2
myprojects/kimi/.env.example
Normal file
2
myprojects/kimi/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
# No env vars required for basic kimi CLI usage
|
||||
# Add API keys or config here as needed
|
||||
18
myprojects/kimi/Dockerfile
Normal file
18
myprojects/kimi/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache bash curl
|
||||
|
||||
RUN addgroup -g 1000 kimi && \
|
||||
adduser -D -u 1000 -G kimi kimi
|
||||
|
||||
USER kimi
|
||||
WORKDIR /home/kimi
|
||||
|
||||
RUN curl -fsSL https://astral.sh/uv/install.sh | sh
|
||||
RUN /home/kimi/.local/bin/uv tool install --python 3.13 kimi-cli
|
||||
|
||||
ENV PATH="/home/kimi/.local/bin:${PATH}"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
ENTRYPOINT ["kimi"]
|
||||
12
myprojects/kimi/compose.yml
Normal file
12
myprojects/kimi/compose.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
services:
|
||||
kimi:
|
||||
build:
|
||||
context: .
|
||||
network: host
|
||||
image: kimi:alpine
|
||||
container_name: kimi
|
||||
hostname: kimi
|
||||
stdin_open: true
|
||||
tty: true
|
||||
volumes:
|
||||
- ./workspace:/workspace
|
||||
6
remote/ttyd/.env.example
Normal file
6
remote/ttyd/.env.example
Normal file
@@ -0,0 +1,6 @@
|
||||
DOMAIN_NAME=
|
||||
SUBDOMAIN=
|
||||
TAG=
|
||||
GENERIC_TIMEZONE=
|
||||
IP_ADDRESS=
|
||||
TERM=xterm-256color
|
||||
20
remote/ttyd/Dockerfile
Normal file
20
remote/ttyd/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM tsl0922/ttyd:alpine
|
||||
|
||||
# Install ping and ssh client
|
||||
RUN apk update && apk add --no-cache \
|
||||
iputils \
|
||||
openssh-client \
|
||||
curl \
|
||||
bind-tools \
|
||||
bash-completion \
|
||||
coreutils
|
||||
|
||||
RUN rm -rf /var/cache/apk/*
|
||||
|
||||
RUN addgroup -g 1000 ttyd && \
|
||||
adduser -D -u 1000 -G ttyd tty
|
||||
|
||||
USER 1000:1000
|
||||
|
||||
# Set default command to start ttyd with bash
|
||||
CMD ["ttyd", "/bin/bash"]
|
||||
45
remote/ttyd/compose.yml
Normal file
45
remote/ttyd/compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
services:
|
||||
ttyd:
|
||||
build:
|
||||
context: .
|
||||
network: host
|
||||
image: ttyd:alpine
|
||||
container_name: ttyd
|
||||
hostname: ttyd
|
||||
restart: unless-stopped
|
||||
command: ["ttyd", "-W", "-o", "-u", "1000", "-g", "1000", "bash"]
|
||||
stdin_open: true
|
||||
tty: true
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.$SUBDOMAIN.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
- traefik.http.routers.$SUBDOMAIN.tls=true
|
||||
- traefik.http.routers.$SUBDOMAIN.entrypoints=web,websecure
|
||||
- traefik.http.routers.$SUBDOMAIN.tls.certresolver=cloudflare
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLRedirect=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSSeconds=315360000
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.browserXSSFilter=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.contentTypeNosniff=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.forceSTSHeader=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLHost=${DOMAIN_NAME}
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSIncludeSubdomains=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSPreload=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.frameDeny=true
|
||||
- traefik.http.routers.$SUBDOMAIN.middlewares=authentik-forwardauth@docker
|
||||
# - traefik.http.routers.$SUBDOMAIN.middlewares=$SUBDOMAIN@docker
|
||||
- traefik.http.services.$SUBDOMAIN.loadbalancer.server.port=7681
|
||||
- traefik.docker.network=jump
|
||||
env_file:
|
||||
- .env
|
||||
# volumes:
|
||||
networks:
|
||||
jump:
|
||||
lan:
|
||||
ipv4_address: $IP_ADDRESS
|
||||
user: "1000:1000"
|
||||
# volumes:
|
||||
networks:
|
||||
jump:
|
||||
external: true
|
||||
lan:
|
||||
external: true
|
||||
2
webapp/it-tools-sharevb/.env.example
Normal file
2
webapp/it-tools-sharevb/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
DOMAIN_NAME=
|
||||
SUBDOMAIN=
|
||||
32
webapp/it-tools-sharevb/compose.yml
Normal file
32
webapp/it-tools-sharevb/compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
services:
|
||||
it-tools:
|
||||
image: sharevb/it-tools:latest
|
||||
container_name: it-tools
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.$SUBDOMAIN.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
- traefik.http.routers.$SUBDOMAIN.tls=true
|
||||
- traefik.http.routers.$SUBDOMAIN.entrypoints=web,websecure
|
||||
- traefik.http.routers.$SUBDOMAIN.tls.certresolver=cloudflare
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLRedirect=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSSeconds=315360000
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.browserXSSFilter=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.contentTypeNosniff=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.forceSTSHeader=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLHost=${DOMAIN_NAME}
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSIncludeSubdomains=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSPreload=true
|
||||
- traefik.http.routers.$SUBDOMAIN.middlewares=$SUBDOMAIN@docker
|
||||
- traefik.http.services.$SUBDOMAIN.loadbalancer.server.port=8080
|
||||
networks:
|
||||
- webapp
|
||||
networks:
|
||||
webapp:
|
||||
external:
|
||||
true
|
||||
@@ -2,6 +2,12 @@ services:
|
||||
it-tools:
|
||||
image: corentinth/it-tools
|
||||
container_name: it-tools
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://127.0.0.1/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.$SUBDOMAIN.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
|
||||
2
webapp/n8n/.gitignore
vendored
Normal file
2
webapp/n8n/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.env
|
||||
local-files
|
||||
@@ -30,7 +30,8 @@ services:
|
||||
- ./local-files:/files
|
||||
networks:
|
||||
- webapp
|
||||
- db
|
||||
- backend
|
||||
- mcp
|
||||
secrets:
|
||||
DB_PASS:
|
||||
file: .secrets/DB_PASS
|
||||
@@ -38,7 +39,10 @@ networks:
|
||||
webapp:
|
||||
external:
|
||||
true
|
||||
db:
|
||||
backend:
|
||||
external:
|
||||
true
|
||||
mcp:
|
||||
external:
|
||||
true
|
||||
volumes:
|
||||
|
||||
3
webapp/navidrome/.gitignore
vendored
Normal file
3
webapp/navidrome/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
data/
|
||||
music/
|
||||
music
|
||||
27
webapp/super-productivity/compose.yml
Normal file
27
webapp/super-productivity/compose.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
services:
|
||||
super-productivity:
|
||||
image: johannesjo/super-productivity:latest
|
||||
container_name: super-productivity
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.$SUBDOMAIN.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
|
||||
- traefik.http.routers.$SUBDOMAIN.tls=true
|
||||
- traefik.http.routers.$SUBDOMAIN.entrypoints=web,websecure
|
||||
- traefik.http.routers.$SUBDOMAIN.tls.certresolver=cloudflare
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLRedirect=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSSeconds=315360000
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.browserXSSFilter=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.contentTypeNosniff=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.forceSTSHeader=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.SSLHost=${DOMAIN_NAME}
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSIncludeSubdomains=true
|
||||
- traefik.http.middlewares.$SUBDOMAIN.headers.STSPreload=true
|
||||
- traefik.http.routers.$SUBDOMAIN.middlewares=authentik-forwardauth@docker
|
||||
- traefik.http.services.$SUBDOMAIN.loadbalancer.server.port=80
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- webapp
|
||||
networks:
|
||||
webapp:
|
||||
external:
|
||||
true
|
||||
Reference in New Issue
Block a user