Initial commit: 23 docker stacks for GitOps deployment
Stacks included:
- Infrastructure: traefik, authentik, gitea, registry, watchtower, dockge
- Monitoring: smokeping, changedetection
- Apps: ghost, gollum, wallabag, radicale, invidious, xbackbone, filebrowser, syncthing, zerotier
- Custom: obsidian-tools, memento, perilous, ramz, bookclub, brain
🤖 Generated with Claude Code
This commit is contained in:
commit
4dbb0b9180
114
.gitea/workflows/deploy.yml
Normal file
114
.gitea/workflows/deploy.yml
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
name: Deploy Stacks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'stacks/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
stack:
|
||||||
|
description: 'Stack to deploy (or "all")'
|
||||||
|
required: true
|
||||||
|
default: 'all'
|
||||||
|
|
||||||
|
env:
|
||||||
|
STACKS_DIR: /var/core
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect-changes:
|
||||||
|
runs-on: ubuntu-prod
|
||||||
|
outputs:
|
||||||
|
stacks: ${{ steps.changes.outputs.stacks }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Find changed stacks
|
||||||
|
id: changes
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
if [ "${{ github.event.inputs.stack }}" = "all" ]; then
|
||||||
|
STACKS=$(ls stacks/ | tr '\n' ' ')
|
||||||
|
else
|
||||||
|
STACKS="${{ github.event.inputs.stack }}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
STACKS=$(git diff --name-only HEAD~1 HEAD | grep '^stacks/' | cut -d'/' -f2 | sort -u | tr '\n' ' ')
|
||||||
|
fi
|
||||||
|
echo "stacks=$STACKS" >> $GITHUB_OUTPUT
|
||||||
|
echo "Detected stacks to deploy: $STACKS"
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: detect-changes
|
||||||
|
if: needs.detect-changes.outputs.stacks != ''
|
||||||
|
runs-on: ubuntu-prod
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
stack: ${{ fromJson(format('["{0}"]', join(fromJson(format('["{0}"]', replace(needs.detect-changes.outputs.stacks, ' ', '","'))), '","'))) }}
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create .env file
|
||||||
|
run: |
|
||||||
|
cd stacks/${{ matrix.stack }}
|
||||||
|
if [ -f ".env.template" ]; then
|
||||||
|
envsubst < .env.template > .env
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
# Global
|
||||||
|
DOMAIN: ${{ secrets.DOMAIN }}
|
||||||
|
VOLUMES_ROOT: ${{ secrets.VOLUMES_ROOT }}
|
||||||
|
# Traefik
|
||||||
|
ACME_EMAIL: ${{ secrets.ACME_EMAIL }}
|
||||||
|
# Authentik
|
||||||
|
AUTHENTIK_SECRET_KEY: ${{ secrets.AUTHENTIK_SECRET_KEY }}
|
||||||
|
AUTHENTIK_PG_PASS: ${{ secrets.AUTHENTIK_PG_PASS }}
|
||||||
|
# Immich
|
||||||
|
IMMICH_DB_PASSWORD: ${{ secrets.IMMICH_DB_PASSWORD }}
|
||||||
|
# Planka
|
||||||
|
PLANKA_SECRET_KEY: ${{ secrets.PLANKA_SECRET_KEY }}
|
||||||
|
PLANKA_OIDC_CLIENT_ID: ${{ secrets.PLANKA_OIDC_CLIENT_ID }}
|
||||||
|
PLANKA_OIDC_CLIENT_SECRET: ${{ secrets.PLANKA_OIDC_CLIENT_SECRET }}
|
||||||
|
# Registry
|
||||||
|
REGISTRY_HTTP_SECRET: ${{ secrets.REGISTRY_HTTP_SECRET }}
|
||||||
|
REGISTRY_HTPASSWD: ${{ secrets.REGISTRY_HTPASSWD }}
|
||||||
|
# Memento
|
||||||
|
MEMENTO_AUTH_SECRET: ${{ secrets.MEMENTO_AUTH_SECRET }}
|
||||||
|
MEMENTO_AUTHENTIK_CLIENT_ID: ${{ secrets.MEMENTO_AUTHENTIK_CLIENT_ID }}
|
||||||
|
MEMENTO_AUTHENTIK_CLIENT_SECRET: ${{ secrets.MEMENTO_AUTHENTIK_CLIENT_SECRET }}
|
||||||
|
# Bookclub
|
||||||
|
BOOKCLUB_SMTP_HOST: ${{ secrets.BOOKCLUB_SMTP_HOST }}
|
||||||
|
BOOKCLUB_SMTP_USER: ${{ secrets.BOOKCLUB_SMTP_USER }}
|
||||||
|
BOOKCLUB_SMTP_PASS: ${{ secrets.BOOKCLUB_SMTP_PASS }}
|
||||||
|
BOOKCLUB_MAIL_FROM: ${{ secrets.BOOKCLUB_MAIL_FROM }}
|
||||||
|
BOOKCLUB_MAIL_TO: ${{ secrets.BOOKCLUB_MAIL_TO }}
|
||||||
|
BOOKCLUB_SECRET_PHRASE: ${{ secrets.BOOKCLUB_SECRET_PHRASE }}
|
||||||
|
# Perilous
|
||||||
|
PERILOUS_CODE_SERVER_PASSWORD: ${{ secrets.PERILOUS_CODE_SERVER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Deploy ${{ matrix.stack }}
|
||||||
|
run: |
|
||||||
|
STACK_DIR="${{ env.STACKS_DIR }}/${{ matrix.stack }}"
|
||||||
|
|
||||||
|
# Create stack directory if needed
|
||||||
|
mkdir -p "$STACK_DIR"
|
||||||
|
|
||||||
|
# Copy files to stack directory
|
||||||
|
cp -r stacks/${{ matrix.stack }}/* "$STACK_DIR/"
|
||||||
|
|
||||||
|
cd "$STACK_DIR"
|
||||||
|
|
||||||
|
# Pull and deploy
|
||||||
|
docker compose pull --ignore-pull-failures || true
|
||||||
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
|
echo "✅ Deployed ${{ matrix.stack }}"
|
||||||
|
|
||||||
|
- name: Verify deployment
|
||||||
|
run: |
|
||||||
|
sleep 5
|
||||||
|
cd ${{ env.STACKS_DIR }}/${{ matrix.stack }}
|
||||||
|
docker compose ps
|
||||||
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Environment files (secrets are in Gitea)
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
!.env.template
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Data directories
|
||||||
|
*/data/
|
||||||
|
*/postgres/
|
||||||
|
*/redis/
|
||||||
|
*/config/
|
||||||
|
*/upload/
|
||||||
|
*/media/
|
||||||
|
*/certs/
|
||||||
|
acme.json
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor files
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
141
README.md
Normal file
141
README.md
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# Docker Stacks
|
||||||
|
|
||||||
|
GitOps-managed Docker Compose stacks. Push changes to `main` branch and Gitea Actions will automatically deploy.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
stacks/
|
||||||
|
├── traefik/ # Reverse proxy + SSL
|
||||||
|
├── authentik/ # SSO/Identity provider
|
||||||
|
├── registry/ # Docker registry
|
||||||
|
├── immich/ # Photo management
|
||||||
|
├── planka/ # Kanban boards
|
||||||
|
├── syncthing/ # File sync
|
||||||
|
├── filebrowser/ # Web file manager
|
||||||
|
├── memento/ # Custom app
|
||||||
|
├── obsidian-tools/ # Obsidian vault tools
|
||||||
|
├── perilous/ # Blog/website
|
||||||
|
├── ramz/ # Go web app
|
||||||
|
├── bookclub/ # Form mailer
|
||||||
|
├── watchtower/ # Auto container updates
|
||||||
|
├── dockge/ # Container management UI
|
||||||
|
└── smokeping/ # Network monitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. Edit compose files in `stacks/<service>/`
|
||||||
|
2. Commit and push to `main`
|
||||||
|
3. Gitea Actions detects changed stacks
|
||||||
|
4. Deploys only the changed stacks to `/var/core/<service>/`
|
||||||
|
|
||||||
|
## Manual Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy single stack
|
||||||
|
./scripts/deploy.sh traefik
|
||||||
|
|
||||||
|
# Deploy all stacks
|
||||||
|
./scripts/deploy.sh all
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Gitea Secrets
|
||||||
|
|
||||||
|
Set these in Gitea → Repository → Settings → Actions → Secrets:
|
||||||
|
|
||||||
|
### Global
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `DOMAIN` | Base domain (e.g., `ghost.tel`) |
|
||||||
|
| `VOLUMES_ROOT` | Data root path (e.g., `/var/core`) |
|
||||||
|
| `ACME_EMAIL` | Email for Let's Encrypt |
|
||||||
|
|
||||||
|
### Authentik
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `AUTHENTIK_SECRET_KEY` | Generate: `openssl rand -hex 50` |
|
||||||
|
| `AUTHENTIK_PG_PASS` | PostgreSQL password |
|
||||||
|
|
||||||
|
### Immich
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `IMMICH_DB_PASSWORD` | PostgreSQL password |
|
||||||
|
|
||||||
|
### Planka
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `PLANKA_SECRET_KEY` | Generate: `openssl rand -hex 64` |
|
||||||
|
| `PLANKA_OIDC_CLIENT_ID` | Authentik client ID |
|
||||||
|
| `PLANKA_OIDC_CLIENT_SECRET` | Authentik client secret |
|
||||||
|
|
||||||
|
### Registry
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `REGISTRY_HTTP_SECRET` | Generate: `openssl rand -hex 32` |
|
||||||
|
|
||||||
|
### Memento
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `MEMENTO_AUTH_SECRET` | Auth.js secret |
|
||||||
|
| `MEMENTO_AUTHENTIK_CLIENT_ID` | Authentik client ID |
|
||||||
|
| `MEMENTO_AUTHENTIK_CLIENT_SECRET` | Authentik client secret |
|
||||||
|
|
||||||
|
### Bookclub
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `BOOKCLUB_SMTP_HOST` | SMTP server |
|
||||||
|
| `BOOKCLUB_SMTP_USER` | SMTP username |
|
||||||
|
| `BOOKCLUB_SMTP_PASS` | SMTP password |
|
||||||
|
| `BOOKCLUB_MAIL_FROM` | From email |
|
||||||
|
| `BOOKCLUB_MAIL_TO` | Recipient email |
|
||||||
|
| `BOOKCLUB_SECRET_PHRASE` | Form submission secret |
|
||||||
|
|
||||||
|
### Perilous
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `PERILOUS_CODE_SERVER_PASSWORD` | Code-server password |
|
||||||
|
|
||||||
|
## Runner Setup
|
||||||
|
|
||||||
|
The workflow requires a self-hosted runner on the prod server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On ubuntu-prod, register a Gitea runner
|
||||||
|
# See: https://docs.gitea.com/usage/actions/act-runner
|
||||||
|
|
||||||
|
# Install act_runner
|
||||||
|
wget https://gitea.com/gitea/act_runner/releases/download/v0.2.6/act_runner-0.2.6-linux-amd64
|
||||||
|
chmod +x act_runner-*
|
||||||
|
sudo mv act_runner-* /usr/local/bin/act_runner
|
||||||
|
|
||||||
|
# Register with Gitea
|
||||||
|
act_runner register --no-interactive \
|
||||||
|
--instance https://gitea.ghost.tel \
|
||||||
|
--token <runner-token> \
|
||||||
|
--name ubuntu-prod \
|
||||||
|
--labels ubuntu-prod
|
||||||
|
|
||||||
|
# Run as service
|
||||||
|
act_runner daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
## First-Time Setup
|
||||||
|
|
||||||
|
1. Create the `web` Docker network:
|
||||||
|
```bash
|
||||||
|
docker network create web
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create `acme.json` for Traefik:
|
||||||
|
```bash
|
||||||
|
touch /var/core/traefik/acme.json
|
||||||
|
chmod 600 /var/core/traefik/acme.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Deploy traefik first:
|
||||||
|
```bash
|
||||||
|
./scripts/deploy.sh traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Then deploy other stacks as needed.
|
||||||
48
scripts/deploy.sh
Executable file
48
scripts/deploy.sh
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Manual deploy script for docker-stacks
|
||||||
|
# Usage: ./deploy.sh [stack-name|all]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
STACKS_DIR="/var/core"
|
||||||
|
|
||||||
|
deploy_stack() {
|
||||||
|
local stack=$1
|
||||||
|
echo "🚀 Deploying $stack..."
|
||||||
|
|
||||||
|
local src="$REPO_ROOT/stacks/$stack"
|
||||||
|
local dest="$STACKS_DIR/$stack"
|
||||||
|
|
||||||
|
if [ ! -d "$src" ]; then
|
||||||
|
echo "❌ Stack $stack not found in $src"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create destination directory
|
||||||
|
mkdir -p "$dest"
|
||||||
|
|
||||||
|
# Copy files
|
||||||
|
cp -r "$src"/* "$dest/"
|
||||||
|
|
||||||
|
# Deploy
|
||||||
|
cd "$dest"
|
||||||
|
docker compose pull --ignore-pull-failures 2>/dev/null || true
|
||||||
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
|
echo "✅ Deployed $stack"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ "$1" = "all" ]; then
|
||||||
|
echo "Deploying all stacks..."
|
||||||
|
for stack_dir in "$REPO_ROOT/stacks"/*/; do
|
||||||
|
stack=$(basename "$stack_dir")
|
||||||
|
deploy_stack "$stack"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
deploy_stack "$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 Deployment complete!"
|
||||||
3
stacks/authentik/.env.template
Normal file
3
stacks/authentik/.env.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY}
|
||||||
|
AUTHENTIK_PG_PASS=${AUTHENTIK_PG_PASS}
|
||||||
75
stacks/authentik/docker-compose.yml
Normal file
75
stacks/authentik/docker-compose.yml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: ghcr.io/goauthentik/server:latest
|
||||||
|
container_name: authentik-server
|
||||||
|
restart: unless-stopped
|
||||||
|
command: server
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||||
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS}
|
||||||
|
volumes:
|
||||||
|
- ./media:/media
|
||||||
|
- ./custom-templates:/templates
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
depends_on:
|
||||||
|
- postgresql
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.authentik.entrypoints=https"
|
||||||
|
- "traefik.http.routers.authentik.rule=Host(`authentik.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.authentik.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.authentik.loadbalancer.server.port=9000"
|
||||||
|
|
||||||
|
worker:
|
||||||
|
image: ghcr.io/goauthentik/server:latest
|
||||||
|
container_name: authentik-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
command: worker
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
|
||||||
|
AUTHENTIK_REDIS__HOST: redis
|
||||||
|
AUTHENTIK_POSTGRESQL__HOST: postgresql
|
||||||
|
AUTHENTIK_POSTGRESQL__USER: authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__NAME: authentik
|
||||||
|
AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_PG_PASS}
|
||||||
|
volumes:
|
||||||
|
- ./media:/media
|
||||||
|
- ./certs:/certs
|
||||||
|
- ./custom-templates:/templates
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
depends_on:
|
||||||
|
- postgresql
|
||||||
|
- redis
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: authentik-postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${AUTHENTIK_PG_PASS}
|
||||||
|
POSTGRES_USER: authentik
|
||||||
|
POSTGRES_DB: authentik
|
||||||
|
volumes:
|
||||||
|
- ./postgres:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: authentik-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: --save 60 1 --loglevel warning
|
||||||
|
volumes:
|
||||||
|
- ./redis:/data
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
14
stacks/bookclub/.env.template
Normal file
14
stacks/bookclub/.env.template
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
SMTP_HOST=${BOOKCLUB_SMTP_HOST}
|
||||||
|
SMTP_PORT=465
|
||||||
|
SMTP_SECURE=true
|
||||||
|
SMTP_USER=${BOOKCLUB_SMTP_USER}
|
||||||
|
SMTP_PASS=${BOOKCLUB_SMTP_PASS}
|
||||||
|
MAIL_FROM=${BOOKCLUB_MAIL_FROM}
|
||||||
|
MAIL_TO=${BOOKCLUB_MAIL_TO}
|
||||||
|
SECRET_PHRASE=${BOOKCLUB_SECRET_PHRASE}
|
||||||
|
THANK_YOU_URL=/thanks.html
|
||||||
|
BIND_HOST=0.0.0.0
|
||||||
|
BIND_PORT=3000
|
||||||
|
TRUST_PROXY=true
|
||||||
|
TZ=America/New_York
|
||||||
7
stacks/bookclub/Dockerfile
Normal file
7
stacks/bookclub/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY server.js ./
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
20
stacks/bookclub/docker-compose.yml
Normal file
20
stacks/bookclub/docker-compose.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
form-mailer:
|
||||||
|
build: .
|
||||||
|
container_name: bookclub-form-mailer
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.form-mailer.rule=Host(`bookclub.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.form-mailer.entrypoints=https"
|
||||||
|
- "traefik.http.routers.form-mailer.tls=true"
|
||||||
|
- "traefik.http.routers.form-mailer.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.form-mailer.loadbalancer.server.port=3000"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
13
stacks/bookclub/package.json
Normal file
13
stacks/bookclub/package.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "bookclub-form-mailer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Simple form mailer service",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"nodemailer": "^6.9.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
51
stacks/bookclub/server.js
Normal file
51
stacks/bookclub/server.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
if (process.env.TRUST_PROXY === 'true') {
|
||||||
|
app.set('trust proxy', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.SMTP_HOST,
|
||||||
|
port: parseInt(process.env.SMTP_PORT || '465'),
|
||||||
|
secure: process.env.SMTP_SECURE === 'true',
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USER,
|
||||||
|
pass: process.env.SMTP_PASS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/submit', async (req, res) => {
|
||||||
|
const { secret, ...formData } = req.body;
|
||||||
|
|
||||||
|
if (secret !== process.env.SECRET_PHRASE) {
|
||||||
|
return res.status(403).send('Invalid secret phrase');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.MAIL_FROM,
|
||||||
|
to: process.env.MAIL_TO,
|
||||||
|
subject: 'New Form Submission',
|
||||||
|
text: Object.entries(formData)
|
||||||
|
.map(([key, value]) => `${key}: ${value}`)
|
||||||
|
.join('\n')
|
||||||
|
});
|
||||||
|
|
||||||
|
res.redirect(process.env.THANK_YOU_URL || '/thanks.html');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Mail error:', error);
|
||||||
|
res.status(500).send('Failed to send email');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const port = parseInt(process.env.BIND_PORT || '3000');
|
||||||
|
const host = process.env.BIND_HOST || '0.0.0.0';
|
||||||
|
|
||||||
|
app.listen(port, host, () => {
|
||||||
|
console.log(`Form mailer listening on ${host}:${port}`);
|
||||||
|
});
|
||||||
3
stacks/brain/.env.template
Normal file
3
stacks/brain/.env.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
BRAIN_SFTP_USER=${BRAIN_SFTP_USER}
|
||||||
|
BRAIN_SFTP_PASSWORD=${BRAIN_SFTP_PASSWORD}
|
||||||
32
stacks/brain/docker-compose.yml
Normal file
32
stacks/brain/docker-compose.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: brain
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- 80
|
||||||
|
volumes:
|
||||||
|
- ./public:/usr/share/nginx/html
|
||||||
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.brain.entrypoints=https"
|
||||||
|
- "traefik.http.routers.brain.rule=Host(`brain.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.brain.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.brain.middlewares=auth@file"
|
||||||
|
|
||||||
|
sftp:
|
||||||
|
image: atmoz/sftp
|
||||||
|
container_name: brain-sftp
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./public:/home/commander/upload
|
||||||
|
ports:
|
||||||
|
- "2232:22"
|
||||||
|
command: ${BRAIN_SFTP_USER}:${BRAIN_SFTP_PASSWORD}:1001
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/changedetection/.env.template
Normal file
1
stacks/changedetection/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
40
stacks/changedetection/docker-compose.yml
Normal file
40
stacks/changedetection/docker-compose.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
services:
|
||||||
|
changedetection:
|
||||||
|
image: ghcr.io/dgtlmoon/changedetection.io
|
||||||
|
container_name: changedetection
|
||||||
|
hostname: changedetection
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/datastore
|
||||||
|
environment:
|
||||||
|
- PLAYWRIGHT_DRIVER_URL=ws://playwright-chrome:3000/?stealth=1&--disable-web-security=true
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.changedetection.entrypoints=https"
|
||||||
|
- "traefik.http.routers.changedetection.rule=Host(`change.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.changedetection.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.changedetection.middlewares=auth@file"
|
||||||
|
depends_on:
|
||||||
|
- playwright-chrome
|
||||||
|
|
||||||
|
playwright-chrome:
|
||||||
|
image: dgtlmoon/sockpuppetbrowser:latest
|
||||||
|
hostname: playwright-chrome
|
||||||
|
restart: unless-stopped
|
||||||
|
cap_add:
|
||||||
|
- SYS_ADMIN
|
||||||
|
environment:
|
||||||
|
- SCREEN_WIDTH=1920
|
||||||
|
- SCREEN_HEIGHT=1024
|
||||||
|
- SCREEN_DEPTH=16
|
||||||
|
- MAX_CONCURRENT_CHROME_PROCESSES=10
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/dockge/.env.template
Normal file
1
stacks/dockge/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
25
stacks/dockge/docker-compose.yml
Normal file
25
stacks/dockge/docker-compose.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
dockge:
|
||||||
|
image: louislam/dockge:1
|
||||||
|
container_name: dockge
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 5001:5001
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./data:/app/data
|
||||||
|
- /var/core:/var/core
|
||||||
|
environment:
|
||||||
|
- DOCKGE_STACKS_DIR=/var/core
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.dockge.rule=Host(`dockge.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.dockge.entrypoints=https"
|
||||||
|
- "traefik.http.routers.dockge.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.dockge.loadbalancer.server.port=5001"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
2
stacks/filebrowser/.env.template
Normal file
2
stacks/filebrowser/.env.template
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
FILE_ROOT=/srv
|
||||||
22
stacks/filebrowser/docker-compose.yml
Normal file
22
stacks/filebrowser/docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
filebrowser:
|
||||||
|
image: filebrowser/filebrowser:latest
|
||||||
|
container_name: filebrowser
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./config/filebrowser.db:/database.db
|
||||||
|
- ${FILE_ROOT:-/srv}:/srv
|
||||||
|
expose:
|
||||||
|
- 80
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.filebrowser.entrypoints=https"
|
||||||
|
- "traefik.http.routers.filebrowser.rule=Host(`files.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.filebrowser.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.filebrowser.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
2
stacks/ghost/.env.template
Normal file
2
stacks/ghost/.env.template
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
GHOST_DB_PASSWORD=${GHOST_DB_PASSWORD}
|
||||||
37
stacks/ghost/docker-compose.yml
Normal file
37
stacks/ghost/docker-compose.yml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:4-alpine
|
||||||
|
container_name: ghost
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- 2368
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
environment:
|
||||||
|
database__client: mysql
|
||||||
|
database__connection__host: db
|
||||||
|
database__connection__user: root
|
||||||
|
database__connection__password: ${GHOST_DB_PASSWORD}
|
||||||
|
database__connection__database: ghost
|
||||||
|
url: https://ghost.${DOMAIN}
|
||||||
|
volumes:
|
||||||
|
- ./content:/var/lib/ghost/content
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.ghost.entrypoints=https"
|
||||||
|
- "traefik.http.routers.ghost.rule=Host(`ghost.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.ghost.tls.certresolver=http"
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mysql:5.7
|
||||||
|
container_name: ghost-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: ${GHOST_DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- ./mysql:/var/lib/mysql
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
4
stacks/gitea/.env.template
Normal file
4
stacks/gitea/.env.template
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
GITEA_DB_ROOT_PASSWORD=${GITEA_DB_ROOT_PASSWORD}
|
||||||
|
GITEA_DB_PASSWORD=${GITEA_DB_PASSWORD}
|
||||||
|
GITEA_RUNNER_TOKEN=${GITEA_RUNNER_TOKEN}
|
||||||
58
stacks/gitea/docker-compose.yml
Normal file
58
stacks/gitea/docker-compose.yml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
container_name: gitea
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
|
- "22"
|
||||||
|
ports:
|
||||||
|
- "2245:22"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
environment:
|
||||||
|
- DOMAIN=gitea.${DOMAIN}
|
||||||
|
- SSH_DOMAIN=gitea.${DOMAIN}
|
||||||
|
- SSH_PORT=2245
|
||||||
|
- SSH_LISTEN_PORT=22
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.gitea.entrypoints=https"
|
||||||
|
- "traefik.http.routers.gitea.rule=Host(`gitea.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.gitea.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mariadb:10
|
||||||
|
container_name: gitea-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${GITEA_DB_ROOT_PASSWORD}
|
||||||
|
- MYSQL_DATABASE=gitea
|
||||||
|
- MYSQL_USER=gitea
|
||||||
|
- MYSQL_PASSWORD=${GITEA_DB_PASSWORD}
|
||||||
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/mysql
|
||||||
|
|
||||||
|
runner:
|
||||||
|
image: gitea/act_runner
|
||||||
|
container_name: gitea-runner
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- web
|
||||||
|
volumes:
|
||||||
|
- ./runner-data:/data
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- GITEA_INSTANCE_URL=https://gitea.${DOMAIN}
|
||||||
|
- GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/gollum/.env.template
Normal file
1
stacks/gollum/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
24
stacks/gollum/docker-compose.yml
Normal file
24
stacks/gollum/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
gollum:
|
||||||
|
image: gollumwiki/gollum:master
|
||||||
|
container_name: gollum
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./config.rb:/etc/gollum/config.rb
|
||||||
|
- ./wikidata:/wiki
|
||||||
|
command:
|
||||||
|
- "--config=/etc/gollum/config.rb"
|
||||||
|
expose:
|
||||||
|
- 4567
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.gollum.entrypoints=https"
|
||||||
|
- "traefik.http.routers.gollum.rule=Host(`gollum.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.gollum.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.gollum.middlewares=auth@file"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
5
stacks/invidious/.env.template
Normal file
5
stacks/invidious/.env.template
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
INVIDIOUS_DB_PASSWORD=${INVIDIOUS_DB_PASSWORD}
|
||||||
|
INVIDIOUS_VISITOR_DATA=${INVIDIOUS_VISITOR_DATA}
|
||||||
|
INVIDIOUS_PO_TOKEN=${INVIDIOUS_PO_TOKEN}
|
||||||
|
INVIDIOUS_HMAC_KEY=${INVIDIOUS_HMAC_KEY}
|
||||||
67
stacks/invidious/docker-compose.yml
Normal file
67
stacks/invidious/docker-compose.yml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
services:
|
||||||
|
invidious:
|
||||||
|
image: quay.io/invidious/invidious:latest
|
||||||
|
container_name: invidious
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3001:3000"
|
||||||
|
environment:
|
||||||
|
INVIDIOUS_CONFIG: |
|
||||||
|
db:
|
||||||
|
dbname: invidious
|
||||||
|
user: kemal
|
||||||
|
password: ${INVIDIOUS_DB_PASSWORD}
|
||||||
|
host: invidious-db
|
||||||
|
port: 5432
|
||||||
|
check_tables: true
|
||||||
|
signature_server: inv_sig_helper:12999
|
||||||
|
visitor_data: ${INVIDIOUS_VISITOR_DATA}
|
||||||
|
po_token: ${INVIDIOUS_PO_TOKEN}
|
||||||
|
hmac_key: "${INVIDIOUS_HMAC_KEY}"
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: "1G"
|
||||||
|
max-file: "4"
|
||||||
|
depends_on:
|
||||||
|
- invidious-db
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.services.invidious.loadbalancer.server.port=3000"
|
||||||
|
- "traefik.http.routers.invidious.entrypoints=https"
|
||||||
|
- "traefik.http.routers.invidious.rule=Host(`invid.${DOMAIN}`, `i.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.invidious.tls.certresolver=http"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
|
||||||
|
inv_sig_helper:
|
||||||
|
image: quay.io/invidious/inv-sig-helper:latest
|
||||||
|
container_name: inv-sig-helper
|
||||||
|
command: ["--tcp", "0.0.0.0:12999"]
|
||||||
|
environment:
|
||||||
|
- RUST_LOG=info
|
||||||
|
restart: unless-stopped
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
read_only: true
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
|
||||||
|
invidious-db:
|
||||||
|
image: postgres:14
|
||||||
|
container_name: invidious-db
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./postgres:/var/lib/postgresql/data
|
||||||
|
- ./config/sql:/config/sql
|
||||||
|
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: invidious
|
||||||
|
POSTGRES_USER: kemal
|
||||||
|
POSTGRES_PASSWORD: ${INVIDIOUS_DB_PASSWORD}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
6
stacks/memento/.env.template
Normal file
6
stacks/memento/.env.template
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
AUTH_SECRET=${MEMENTO_AUTH_SECRET}
|
||||||
|
AUTH_AUTHENTIK_CLIENT_ID=${MEMENTO_AUTHENTIK_CLIENT_ID}
|
||||||
|
AUTH_AUTHENTIK_CLIENT_SECRET=${MEMENTO_AUTHENTIK_CLIENT_SECRET}
|
||||||
|
AUTH_AUTHENTIK_ISSUER=https://authentik.${DOMAIN}/application/o/memento
|
||||||
|
AUTH_AUTHENTIK_REDIRECT_URI=https://memento.${DOMAIN}
|
||||||
22
stacks/memento/docker-compose.yml
Normal file
22
stacks/memento/docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: gitea.${DOMAIN}/knight/memento:latest
|
||||||
|
container_name: memento
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- "3002"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.memento.entrypoints=https"
|
||||||
|
- "traefik.http.routers.memento.rule=Host(`memento.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.memento.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.memento.loadbalancer.server.port=3002"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/obsidian-tools/.env.template
Normal file
1
stacks/obsidian-tools/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
87
stacks/obsidian-tools/docker-compose.yml
Normal file
87
stacks/obsidian-tools/docker-compose.yml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
services:
|
||||||
|
obbytodo:
|
||||||
|
image: gitea.${DOMAIN}/knight/obbytodo:latest
|
||||||
|
container_name: obbytodo
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
|
environment:
|
||||||
|
- PORT=3000
|
||||||
|
- VAULT_PATH=/vault
|
||||||
|
- DEFAULT_TASK_FILE=Mobile Tasks.md
|
||||||
|
- DAILY_DIR=log
|
||||||
|
- DAILY_PRIMARY_DIR=Control/log
|
||||||
|
volumes:
|
||||||
|
- ./vault:/vault
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
depends_on:
|
||||||
|
- syncthing
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.entrypoints=https"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.rule=Host(`shell.${DOMAIN}`) && PathPrefix(`/todo`)"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.middlewares=todo-obbytodo-stripprefix@docker,dashboard-auth@file"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.priority=100"
|
||||||
|
- "traefik.http.middlewares.todo-obbytodo-stripprefix.stripPrefix.prefixes=/todo"
|
||||||
|
- "traefik.http.services.todo-obbytodo.loadbalancer.server.port=3000"
|
||||||
|
- "traefik.http.routers.todo-obbytodo.service=todo-obbytodo"
|
||||||
|
- "traefik.http.routers.events-obbytodo.entrypoints=https"
|
||||||
|
- "traefik.http.routers.events-obbytodo.rule=Host(`shell.${DOMAIN}`) && PathPrefix(`/events`)"
|
||||||
|
- "traefik.http.routers.events-obbytodo.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.events-obbytodo.middlewares=dashboard-auth@file"
|
||||||
|
- "traefik.http.routers.events-obbytodo.priority=100"
|
||||||
|
- "traefik.http.routers.events-obbytodo.service=todo-obbytodo"
|
||||||
|
|
||||||
|
search-service:
|
||||||
|
image: gitea.${DOMAIN}/knight/obsidiansearch:latest
|
||||||
|
container_name: obsidian-search-service
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- "3033"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- OBSIDIAN_VAULT_PATH=/data
|
||||||
|
- GRAPH_DATA_PATH=/usr/src/app/graph-data/graph.json
|
||||||
|
volumes:
|
||||||
|
- ./vault:/data:ro
|
||||||
|
- ./graph-data:/usr/src/app/graph-data
|
||||||
|
depends_on:
|
||||||
|
- syncthing
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.http.routers.shell-secure.entrypoints=https"
|
||||||
|
- "traefik.http.routers.shell-secure.rule=Host(`shell.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.shell-secure.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.shell-secure.middlewares=dashboard-auth@file"
|
||||||
|
- "traefik.http.services.shell-secure.loadbalancer.server.port=3033"
|
||||||
|
- "traefik.http.routers.shell-secure.service=shell-secure"
|
||||||
|
|
||||||
|
syncthing:
|
||||||
|
image: lscr.io/linuxserver/syncthing:latest
|
||||||
|
container_name: obsidian-syncthing
|
||||||
|
hostname: obsidian-syncthing
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
volumes:
|
||||||
|
- ./syncthing-config:/config
|
||||||
|
- ./vault:/data1
|
||||||
|
ports:
|
||||||
|
- "8385:8384"
|
||||||
|
- "22001:22000/tcp"
|
||||||
|
- "22001:22000/udp"
|
||||||
|
- "21028:21027/udp"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/perilous/.env.template
Normal file
1
stacks/perilous/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
PERILOUS_CODE_SERVER_PASSWORD=${PERILOUS_CODE_SERVER_PASSWORD}
|
||||||
7
stacks/perilous/Dockerfile
Normal file
7
stacks/perilous/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY server.js ./
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["node", "server.js"]
|
||||||
43
stacks/perilous/docker-compose.yml
Normal file
43
stacks/perilous/docker-compose.yml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: perilous-web
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3003:3000"
|
||||||
|
volumes:
|
||||||
|
- ./content:/usr/src/app/content
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.perilous-secure.entrypoints=https,http"
|
||||||
|
- "traefik.http.routers.perilous-secure.rule=Host(`chapel.perilous.dev`,`forest.perilous.dev`,`castle.perilous.dev`,`siege.perilous.dev`,`pass.perilous.dev`,`perilous.dev`,`www.perilous.dev`,`word.perilous.dev`,`mirror.perilous.dev`,`request.perilous.dev`,`the.chapel.perilous.dev`,`the.forest.perilous.dev`,`the.castle.perilous.dev`,`the.siege.perilous.dev`,`the.pass.perilous.dev`,`the.word.perilous.dev`,`the.mirror.perilous.dev`,`the.request.perilous.dev`,`the.adventure.perilous.dev`,`the.cs.perilous.dev`,`the.gallery.perilous.dev`,`gallery.perilous.dev`,`the.perilous.dev`,`ring.perilous.dev`,`the.ring.perilous.dev`,`autumn.perilous.dev`,`the.autumn.perilous.dev`)"
|
||||||
|
- "traefik.http.routers.perilous-secure.tls.certresolver=http"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
code-server:
|
||||||
|
image: lscr.io/linuxserver/code-server:latest
|
||||||
|
container_name: perilous-code-server
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./content:/home/project
|
||||||
|
- ./config:/config
|
||||||
|
ports:
|
||||||
|
- "8180:8443"
|
||||||
|
environment:
|
||||||
|
- PASSWORD=${PERILOUS_CODE_SERVER_PASSWORD}
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- PROXY_DOMAIN=cs.perilous.dev
|
||||||
|
- DEFAULT_WORKSPACE=/home/project
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.pcs-secure.entrypoints=https,http"
|
||||||
|
- "traefik.http.routers.pcs-secure.rule=Host(`cs.perilous.dev`)"
|
||||||
|
- "traefik.http.routers.pcs-secure.tls.certresolver=http"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
12
stacks/perilous/package.json
Normal file
12
stacks/perilous/package.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "perilous",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Perilous static site server",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
stacks/perilous/server.js
Normal file
24
stacks/perilous/server.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
const CONTENT_DIR = '/usr/src/app/content';
|
||||||
|
|
||||||
|
app.use(express.static(CONTENT_DIR));
|
||||||
|
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
const filePath = path.join(CONTENT_DIR, req.path);
|
||||||
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
|
||||||
|
const indexPath = path.join(filePath, 'index.html');
|
||||||
|
if (fs.existsSync(indexPath)) {
|
||||||
|
return res.sendFile(indexPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.sendFile(path.join(CONTENT_DIR, 'index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Perilous server running on port ${PORT}`);
|
||||||
|
});
|
||||||
1
stacks/radicale/.env.template
Normal file
1
stacks/radicale/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
35
stacks/radicale/docker-compose.yml
Normal file
35
stacks/radicale/docker-compose.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
services:
|
||||||
|
radicale:
|
||||||
|
image: tomsquest/docker-radicale
|
||||||
|
container_name: radicale
|
||||||
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
read_only: true
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- SETUID
|
||||||
|
- SETGID
|
||||||
|
- CHOWN
|
||||||
|
- KILL
|
||||||
|
healthcheck:
|
||||||
|
test: curl -f http://127.0.0.1:5232 || exit 1
|
||||||
|
interval: 30s
|
||||||
|
retries: 3
|
||||||
|
expose:
|
||||||
|
- 5232
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.radicale.entrypoints=https"
|
||||||
|
- "traefik.http.routers.radicale.rule=Host(`radicale.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.radicale.tls.certresolver=http"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
13
stacks/ramz/Dockerfile
Normal file
13
stacks/ramz/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM golang:1.21-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY *.go ./
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o ramz .
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
WORKDIR /root/
|
||||||
|
COPY --from=builder /app/ramz .
|
||||||
|
COPY static/ ./static/
|
||||||
|
EXPOSE 3333
|
||||||
|
CMD ["./ramz"]
|
||||||
24
stacks/ramz/docker-compose.yml
Normal file
24
stacks/ramz/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: ramz
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3333:3333"
|
||||||
|
environment:
|
||||||
|
- GIN_MODE=release
|
||||||
|
volumes:
|
||||||
|
- ./templates:/root/templates
|
||||||
|
- ./data:/root/data
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.ramz-secure.entrypoints=https,http"
|
||||||
|
- "traefik.http.routers.ramz-secure.rule=Host(`parker.ramz.cc`)"
|
||||||
|
- "traefik.http.routers.ramz-secure.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.ramz.loadbalancer.server.port=3333"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
2
stacks/registry/.env.template
Normal file
2
stacks/registry/.env.template
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
REGISTRY_HTTP_SECRET=${REGISTRY_HTTP_SECRET}
|
||||||
27
stacks/registry/docker-compose.yml
Normal file
27
stacks/registry/docker-compose.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
registry:
|
||||||
|
image: registry:2
|
||||||
|
container_name: registry
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/registry
|
||||||
|
- ./auth:/auth
|
||||||
|
environment:
|
||||||
|
REGISTRY_AUTH: htpasswd
|
||||||
|
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
|
||||||
|
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
|
||||||
|
REGISTRY_HTTP_SECRET: ${REGISTRY_HTTP_SECRET}
|
||||||
|
expose:
|
||||||
|
- 5000
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.registry.entrypoints=https"
|
||||||
|
- "traefik.http.routers.registry.rule=Host(`registry.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.registry.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.registry.loadbalancer.server.port=5000"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/smokeping/.env.template
Normal file
1
stacks/smokeping/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
26
stacks/smokeping/docker-compose.yml
Normal file
26
stacks/smokeping/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
smokeping:
|
||||||
|
image: lscr.io/linuxserver/smokeping:latest
|
||||||
|
container_name: smokeping
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- ./data:/data
|
||||||
|
expose:
|
||||||
|
- 80
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.smokeping.entrypoints=https"
|
||||||
|
- "traefik.http.routers.smokeping.rule=Host(`smokeping.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.smokeping.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.smokeping.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/syncthing/.env.template
Normal file
1
stacks/syncthing/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
30
stacks/syncthing/docker-compose.yml
Normal file
30
stacks/syncthing/docker-compose.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
services:
|
||||||
|
syncthing:
|
||||||
|
image: lscr.io/linuxserver/syncthing:latest
|
||||||
|
container_name: syncthing
|
||||||
|
hostname: syncthing
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
- ./data:/data
|
||||||
|
ports:
|
||||||
|
- "8384:8384"
|
||||||
|
- "22000:22000/tcp"
|
||||||
|
- "22000:22000/udp"
|
||||||
|
- "21027:21027/udp"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.syncthing.entrypoints=https"
|
||||||
|
- "traefik.http.routers.syncthing.rule=Host(`syncthing.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.syncthing.tls.certresolver=http"
|
||||||
|
- "traefik.http.services.syncthing.loadbalancer.server.port=8384"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
1
stacks/traefik/.env.template
Normal file
1
stacks/traefik/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
ACME_EMAIL=${ACME_EMAIL}
|
||||||
25
stacks/traefik/docker-compose.yml
Normal file
25
stacks/traefik/docker-compose.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:latest
|
||||||
|
container_name: traefik
|
||||||
|
restart: unless-stopped
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
- 8080:8080
|
||||||
|
environment:
|
||||||
|
- TZ=America/New_York
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ./traefik.yml:/traefik.yml:ro
|
||||||
|
- ./acme.json:/acme.json
|
||||||
|
- ./conf.d/:/conf.d/
|
||||||
|
- /var/log:/var/log
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
40
stacks/traefik/traefik.yml
Normal file
40
stacks/traefik/traefik.yml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
global:
|
||||||
|
checkNewVersion: true
|
||||||
|
|
||||||
|
api:
|
||||||
|
dashboard: true
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
entryPoints:
|
||||||
|
http:
|
||||||
|
address: ":80"
|
||||||
|
https:
|
||||||
|
address: ":443"
|
||||||
|
|
||||||
|
providers:
|
||||||
|
providersThrottleDuration: 2s
|
||||||
|
file:
|
||||||
|
directory: "/conf.d"
|
||||||
|
watch: true
|
||||||
|
docker:
|
||||||
|
watch: true
|
||||||
|
endpoint: "unix:///var/run/docker.sock"
|
||||||
|
exposedByDefault: false
|
||||||
|
network: web
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
http:
|
||||||
|
acme:
|
||||||
|
email: ${ACME_EMAIL}
|
||||||
|
storage: acme.json
|
||||||
|
httpChallenge:
|
||||||
|
entryPoint: http
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: INFO
|
||||||
|
filePath: /var/log/traefik.log
|
||||||
|
format: json
|
||||||
|
|
||||||
|
accessLog:
|
||||||
|
filePath: /var/log/traefik_access.log
|
||||||
|
format: json
|
||||||
3
stacks/wallabag/.env.template
Normal file
3
stacks/wallabag/.env.template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
WALLABAG_DB_PASSWORD=${WALLABAG_DB_PASSWORD}
|
||||||
|
WALLABAG_DB_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}
|
||||||
55
stacks/wallabag/docker-compose.yml
Normal file
55
stacks/wallabag/docker-compose.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
services:
|
||||||
|
wallabag:
|
||||||
|
image: wallabag/wallabag:latest
|
||||||
|
container_name: wallabag
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}
|
||||||
|
- SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
|
||||||
|
- SYMFONY__ENV__DATABASE_HOST=db
|
||||||
|
- SYMFONY__ENV__DATABASE_PORT=3306
|
||||||
|
- SYMFONY__ENV__DATABASE_NAME=wallabag
|
||||||
|
- SYMFONY__ENV__DATABASE_USER=wallabag
|
||||||
|
- SYMFONY__ENV__DATABASE_PASSWORD=${WALLABAG_DB_PASSWORD}
|
||||||
|
- SYMFONY__ENV__DATABASE_CHARSET=utf8mb4
|
||||||
|
- SYMFONY__ENV__MAILER_HOST=127.0.0.1
|
||||||
|
- SYMFONY__ENV__MAILER_USER=~
|
||||||
|
- SYMFONY__ENV__MAILER_PASSWORD=~
|
||||||
|
- SYMFONY__ENV__FROM_EMAIL=wallabag@${DOMAIN}
|
||||||
|
- SYMFONY__ENV__DOMAIN_NAME=https://wallabag.${DOMAIN}
|
||||||
|
expose:
|
||||||
|
- 80
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
volumes:
|
||||||
|
- ./images:/var/www/wallabag/web/assets/images
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.wallabag.entrypoints=https"
|
||||||
|
- "traefik.http.routers.wallabag.rule=Host(`wallabag.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.wallabag.tls.certresolver=http"
|
||||||
|
- "traefik.http.middlewares.wallabag-header.headers.customRequestHeaders.X-Forwarded-Proto=https"
|
||||||
|
- "traefik.http.routers.wallabag.middlewares=wallabag-header"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
- redis
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
container_name: wallabag-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${WALLABAG_DB_ROOT_PASSWORD}
|
||||||
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
|
volumes:
|
||||||
|
- ./data:/var/lib/mysql
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
container_name: wallabag-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
14
stacks/watchtower/docker-compose.yml
Normal file
14
stacks/watchtower/docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
services:
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower:latest
|
||||||
|
container_name: watchtower
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- TZ=America/New_York
|
||||||
|
- DOCKER_API_VERSION=1.44
|
||||||
|
- WATCHTOWER_SCHEDULE=0 0 4 * * *
|
||||||
|
- WATCHTOWER_CLEANUP=true
|
||||||
|
- WATCHTOWER_INCLUDE_STOPPED=false
|
||||||
|
- WATCHTOWER_REVIVE_STOPPED=false
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
1
stacks/xbackbone/.env.template
Normal file
1
stacks/xbackbone/.env.template
Normal file
@ -0,0 +1 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
26
stacks/xbackbone/docker-compose.yml
Normal file
26
stacks/xbackbone/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
xbackbone:
|
||||||
|
image: lscr.io/linuxserver/xbackbone:latest
|
||||||
|
container_name: xbackbone
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
expose:
|
||||||
|
- 443
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.xbackbone.entrypoints=https"
|
||||||
|
- "traefik.http.routers.xbackbone.rule=Host(`xb.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.xbackbone.tls.certresolver=http"
|
||||||
|
- "traefik.http.middlewares.xbackbone-header.headers.customRequestHeaders.X-Forwarded-Proto=https"
|
||||||
|
- "traefik.http.routers.xbackbone.middlewares=xbackbone-header"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
2
stacks/zerotier/.env.template
Normal file
2
stacks/zerotier/.env.template
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DOMAIN=${DOMAIN}
|
||||||
|
# Copy additional env vars from /var/core/zerotier/denv on old prod
|
||||||
24
stacks/zerotier/docker-compose.yml
Normal file
24
stacks/zerotier/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
services:
|
||||||
|
ztncui:
|
||||||
|
image: keynetworks/ztncui
|
||||||
|
container_name: zerotier-ui
|
||||||
|
restart: always
|
||||||
|
expose:
|
||||||
|
- 3180
|
||||||
|
volumes:
|
||||||
|
- ./ztncui:/opt/key-networks/ztncui/etc
|
||||||
|
- ./zt1:/var/lib/zerotier-one
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.zerotier.entrypoints=https"
|
||||||
|
- "traefik.http.routers.zerotier.rule=Host(`zerotierui.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.zerotier.tls.certresolver=http"
|
||||||
|
- "traefik.http.routers.zerotier.middlewares=dashboard-auth@file"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
Loading…
x
Reference in New Issue
Block a user