Compare commits
10 Commits
31cc759cd8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f953a090d | ||
|
|
96e3c80ffb | ||
|
|
9ac7fefb3d | ||
|
|
a5cd34d9ab | ||
|
|
58cc1b1e92 | ||
|
|
f818bd3eca | ||
|
|
a924adee27 | ||
|
|
47e640b969 | ||
|
|
4b23c27bc1 | ||
|
|
166641be57 |
53
.env.example
53
.env.example
@@ -1,20 +1,13 @@
|
|||||||
# .env.example
|
|
||||||
# Copy this file to .env and update the values below
|
|
||||||
|
|
||||||
## Domain / Timezone
|
## Domain / Timezone
|
||||||
DOMAIN=your-domain.com
|
DOMAIN=your-domain.com
|
||||||
TZ=Your/Timezone
|
DOMAIN_PREFIX=gate
|
||||||
|
|
||||||
|
TZ=Africa/Cairo
|
||||||
|
|
||||||
## ACME (Let's Encrypt)
|
## ACME (Let's Encrypt)
|
||||||
# Your email address for Let's Encrypt certificate notifications
|
# Your email address for Let's Encrypt certificate notifications
|
||||||
ACME_EMAIL=admin@your-domain.com
|
ACME_EMAIL=admin@your-domain.com
|
||||||
|
|
||||||
## Basic Authentication
|
|
||||||
# Generate password hash with: echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
|
|
||||||
# Or use online generator: https://hostingcanada.org/htpasswd-generator/
|
|
||||||
# Format: username:$$2y$$10$$hashed_password
|
|
||||||
BASIC_AUTH_USERS=admin:$$2y$$05$$HIIpotyLuLRMBX3sfUs6E.YM3lP9cpF7hK7sHaSXGs6mw/RS6MXCa
|
|
||||||
|
|
||||||
## Umami (PostgreSQL)
|
## Umami (PostgreSQL)
|
||||||
# Database user for Umami analytics
|
# Database user for Umami analytics
|
||||||
UMAMI_DB_USER=umami
|
UMAMI_DB_USER=umami
|
||||||
@@ -24,8 +17,6 @@ UMAMI_DB_PASS=your_strong_database_password
|
|||||||
UMAMI_DB_NAME=umami
|
UMAMI_DB_NAME=umami
|
||||||
# Random 64-character secret for Umami app security
|
# Random 64-character secret for Umami app security
|
||||||
UMAMI_APP_SECRET=your_64_character_random_secret_here
|
UMAMI_APP_SECRET=your_64_character_random_secret_here
|
||||||
# Website ID from Umami dashboard (use placeholder initially, update after setup)
|
|
||||||
UMAMI_WEBSITE_ID=placeholder-website-id
|
|
||||||
|
|
||||||
## pgAdmin
|
## pgAdmin
|
||||||
# Default email for pgAdmin login
|
# Default email for pgAdmin login
|
||||||
@@ -33,8 +24,36 @@ PGADMIN_DEFAULT_EMAIL=admin@your-domain.com
|
|||||||
# Strong password for pgAdmin login
|
# Strong password for pgAdmin login
|
||||||
PGADMIN_DEFAULT_PASSWORD=your_strong_pgadmin_password
|
PGADMIN_DEFAULT_PASSWORD=your_strong_pgadmin_password
|
||||||
|
|
||||||
## Grafana
|
## Authelia
|
||||||
# Admin username for Grafana dashboard
|
# Database name for Authelia
|
||||||
GRAFANA_ADMIN_USER=admin
|
AUTHELIA_DB_NAME=authelia
|
||||||
# Strong password for Grafana admin user
|
# Database user for Authelia
|
||||||
GRAFANA_ADMIN_PASS=your_strong_grafana_password
|
AUTHELIA_DB_USER=authelia
|
||||||
|
# Database password for Authelia PostgreSQL
|
||||||
|
AUTHELIA_DB_PASSWORD=your_authelia_db_password
|
||||||
|
# JWT secret for Authelia (64+ character random string)
|
||||||
|
AUTHELIA_JWT_SECRET=your_64_character_jwt_secret_here
|
||||||
|
# Session secret for Authelia (64+ character random string)
|
||||||
|
AUTHELIA_SESSION_SECRET=your_64_character_session_secret_here
|
||||||
|
# Storage encryption key for Authelia (64+ character random string)
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=your_storage_encryption_key_here
|
||||||
|
# SMTP password for Authelia notifier
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD=your_smtp_password_here
|
||||||
|
|
||||||
|
## Gitea
|
||||||
|
# Database user for Gitea
|
||||||
|
GITEA_DB_USER=gitea
|
||||||
|
# Database password for Gitea PostgreSQL
|
||||||
|
GITEA_DB_PASSWORD=your_gitea_db_password_here
|
||||||
|
# Database name for Gitea
|
||||||
|
GITEA_DB_NAME=gitea
|
||||||
|
# Secret key for Gitea (64+ character random string - generate with: openssl rand -hex 32)
|
||||||
|
GITEA_SECRET_KEY=your_64_character_gitea_secret_key_here
|
||||||
|
# Internal token for Gitea (generate with: docker run -it --rm docker.gitea.com/gitea:1 gitea generate secret INTERNAL_TOKEN)
|
||||||
|
GITEA_INTERNAL_TOKEN=your_gitea_internal_token_here
|
||||||
|
|
||||||
|
## Duplicati
|
||||||
|
# Settings encryption key for Duplicati (random string for encrypting settings database)
|
||||||
|
DUPLICATI_ENCRYPTION_KEY=your_duplicati_encryption_key_here
|
||||||
|
# Web UI password for Duplicati
|
||||||
|
DUPLICATI_PASSWORD=your_duplicati_password_here
|
||||||
|
|||||||
60
.github/workflows/deploy.yml
vendored
60
.github/workflows/deploy.yml
vendored
@@ -1,60 +0,0 @@
|
|||||||
# .github/workflows/deploy.yml
|
|
||||||
name: Deploy Gateway to VPS
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- "docker-compose.yml"
|
|
||||||
- ".github/workflows/deploy.yml"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REMOTE_DIR: ${{ secrets.REMOTE_DIR }}
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: deploy-prod
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: Ship to VPS
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: production
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup SSH key
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
|
|
||||||
chmod 600 ~/.ssh/id_rsa
|
|
||||||
ssh-keyscan -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_HOST }}" >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry on VPS
|
|
||||||
run: |
|
|
||||||
ssh -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" \
|
|
||||||
"echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin"
|
|
||||||
|
|
||||||
- name: Deploy (pull, up, prune)
|
|
||||||
run: |
|
|
||||||
ssh -p "${{ secrets.SSH_PORT }}" "${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}" \
|
|
||||||
"export REMOTE_DIR='${{ secrets.REMOTE_DIR }}'; bash -se" <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
cd "$REMOTE_DIR"
|
|
||||||
|
|
||||||
git pull origin main
|
|
||||||
docker compose pull
|
|
||||||
docker compose up -d --remove-orphans
|
|
||||||
docker image prune -af || true
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Post-deploy smoke checks
|
|
||||||
run: |
|
|
||||||
echo "Deployed to ${{ secrets.SSH_HOST }}:${{ secrets.SSH_PORT }} → ${REMOTE_DIR}"
|
|
||||||
echo "Traefik: https://traefik.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Portainer: https://portainer.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Uptime Kuma: https://uptime.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "Umami: https://umami.gate.${{ secrets.DOMAIN }}"
|
|
||||||
echo "pgAdmin: https://pgadmin.gate.${{ secrets.DOMAIN }}"
|
|
||||||
70
README.md
70
README.md
@@ -1,28 +1,37 @@
|
|||||||
# Launchpad Gateway
|
# Launchpad Gateway
|
||||||
|
|
||||||
A production-ready Traefik-based reverse proxy gateway with automatic SSL/TLS, analytics, monitoring, and container management.
|
A production-ready Traefik-based reverse proxy gateway with automatic SSL/TLS, analytics, monitoring, authentication, and container management.
|
||||||
|
|
||||||
## 🚀 Features
|
## 🚀 Features
|
||||||
|
|
||||||
- **Automatic SSL/TLS** certificates via Let's Encrypt
|
- **Automatic SSL/TLS** certificates via Let's Encrypt
|
||||||
- **Reverse Proxy** with Traefik v3.1
|
- **Reverse Proxy** with Traefik v3.1
|
||||||
|
- **Authentication** with Authelia (2FA, password reset)
|
||||||
- **Web Analytics** with Umami and PostgreSQL
|
- **Web Analytics** with Umami and PostgreSQL
|
||||||
- **Container Management** via Portainer
|
- **Container Management** via Portainer
|
||||||
- **Uptime Monitoring** with Uptime Kuma
|
- **Uptime Monitoring** with Uptime Kuma
|
||||||
- **Security Headers** and Basic Authentication
|
- **Server Monitoring** with Beszel
|
||||||
|
- **Git Repository Hosting** with Gitea
|
||||||
|
- **Encrypted Backups** with Duplicati
|
||||||
|
- **Security Headers** and Flexible Routing
|
||||||
- **Flexible Domain Routing** (subdomains, paths, custom rules)
|
- **Flexible Domain Routing** (subdomains, paths, custom rules)
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
Internet → Traefik (Port 80/443) → Internal Services (traefik_proxy network)
|
Internet → Traefik (Port 80/443) → Authelia (Auth) → Internal Services (traefik_proxy network)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Current Services
|
### Current Services
|
||||||
- **Traefik Dashboard**: `traefik.gate.${DOMAIN}` - Reverse proxy management
|
- **Traefik Dashboard**: `traefik.${DOMAIN_PREFIX}.${DOMAIN}` - Reverse proxy management
|
||||||
- **Portainer**: `portainer.gate.${DOMAIN}` - Docker container management
|
- **Authelia**: `auth.${DOMAIN_PREFIX}.${DOMAIN}` - Authentication portal
|
||||||
- **Uptime Kuma**: `uptime.gate.${DOMAIN}` - Service monitoring
|
- **Portainer**: `portainer.${DOMAIN_PREFIX}.${DOMAIN}` - Docker container management
|
||||||
- **Umami Analytics**: `umami.gate.${DOMAIN}` - Web analytics dashboard
|
- **Uptime Kuma**: `uptime.${DOMAIN_PREFIX}.${DOMAIN}` - Service monitoring
|
||||||
|
- **Umami Analytics**: `umami.${DOMAIN_PREFIX}.${DOMAIN}` - Web analytics dashboard
|
||||||
|
- **pgAdmin**: `pgadmin.${DOMAIN_PREFIX}.${DOMAIN}` - PostgreSQL administration
|
||||||
|
- **Beszel**: `beszel.${DOMAIN_PREFIX}.${DOMAIN}` - Server monitoring
|
||||||
|
- **Gitea**: `git.${DOMAIN_PREFIX}.${DOMAIN}` - Self-hosted Git service (SSH on port 222)
|
||||||
|
- **Duplicati**: `backup.${DOMAIN_PREFIX}.${DOMAIN}` - Encrypted cloud backup
|
||||||
|
|
||||||
## 🛠️ Quick Start
|
## 🛠️ Quick Start
|
||||||
|
|
||||||
@@ -49,19 +58,37 @@ Update `.env` with your settings:
|
|||||||
```bash
|
```bash
|
||||||
# Domain and timezone
|
# Domain and timezone
|
||||||
DOMAIN=your-domain.com
|
DOMAIN=your-domain.com
|
||||||
TZ=Your/Timezone
|
DOMAIN_PREFIX=test
|
||||||
|
TZ=Africa/Cairo
|
||||||
|
|
||||||
# Let's Encrypt email
|
# Let's Encrypt email
|
||||||
ACME_EMAIL=admin@your-domain.com
|
ACME_EMAIL=admin@your-domain.com
|
||||||
|
|
||||||
# Basic auth (generate with: htpasswd -nB admin)
|
|
||||||
BASIC_AUTH_USERS=admin:$$2y$$05$$your_hashed_password
|
|
||||||
|
|
||||||
# Database credentials
|
# Database credentials
|
||||||
UMAMI_DB_USER=umami
|
UMAMI_DB_USER=umami
|
||||||
UMAMI_DB_PASS=your_secure_password
|
UMAMI_DB_PASS=your_secure_password
|
||||||
UMAMI_DB_NAME=umami
|
UMAMI_DB_NAME=umami
|
||||||
UMAMI_APP_SECRET=your_64_character_secret
|
UMAMI_APP_SECRET=your_64_character_secret
|
||||||
|
|
||||||
|
# Authelia secrets (generate with openssl rand -hex 32)
|
||||||
|
AUTHELIA_DB_NAME=authelia
|
||||||
|
AUTHELIA_DB_USER=authelia
|
||||||
|
AUTHELIA_DB_PASSWORD=your_secure_password
|
||||||
|
AUTHELIA_JWT_SECRET=your_64_char_secret
|
||||||
|
AUTHELIA_SESSION_SECRET=your_64_char_secret
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=your_64_char_secret
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD=your_smtp_password
|
||||||
|
|
||||||
|
# Gitea secrets
|
||||||
|
GITEA_DB_USER=gitea
|
||||||
|
GITEA_DB_PASSWORD=your_secure_password
|
||||||
|
GITEA_DB_NAME=gitea
|
||||||
|
GITEA_SECRET_KEY=your_64_char_secret
|
||||||
|
GITEA_INTERNAL_TOKEN=your_internal_token
|
||||||
|
|
||||||
|
# Duplicati secrets
|
||||||
|
DUPLICATI_ENCRYPTION_KEY=your_encryption_key
|
||||||
|
DUPLICATI_PASSWORD=your_backup_password
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -71,10 +98,15 @@ docker compose up -d
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 4. Access Services
|
### 4. Access Services
|
||||||
- **Traefik Dashboard**: `https://traefik.gate.your-domain.com`
|
- **Traefik Dashboard**: `https://traefik.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Portainer**: `https://portainer.gate.your-domain.com`
|
- **Authelia**: `https://auth.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Uptime Kuma**: `https://uptime.gate.your-domain.com`
|
- **Portainer**: `https://portainer.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
- **Umami Analytics**: `https://umami.gate.your-domain.com`
|
- **Uptime Kuma**: `https://uptime.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Umami Analytics**: `https://umami.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **pgAdmin**: `https://pgadmin.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Beszel**: `https://beszel.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
- **Gitea**: `https://git.${DOMAIN_PREFIX}.${DOMAIN}` (SSH: port 222)
|
||||||
|
- **Duplicati**: `https://backup.${DOMAIN_PREFIX}.${DOMAIN}`
|
||||||
|
|
||||||
## 📋 Adding New Services
|
## 📋 Adding New Services
|
||||||
|
|
||||||
@@ -231,16 +263,15 @@ services:
|
|||||||
|
|
||||||
### Available Middlewares
|
### Available Middlewares
|
||||||
- **`security-headers`**: HSTS, XSS protection, content type sniffing prevention
|
- **`security-headers`**: HSTS, XSS protection, content type sniffing prevention
|
||||||
- **`basic-auth`**: HTTP Basic Authentication for admin interfaces
|
- **`authelia`**: Authentication and authorization with 2FA support
|
||||||
- **`umami-analytics`**: Automatic web analytics tracking
|
|
||||||
|
|
||||||
### Usage Examples
|
### Usage Examples
|
||||||
```yaml
|
```yaml
|
||||||
# Public application with analytics
|
# Public application with analytics
|
||||||
- traefik.http.routers.app.middlewares=umami-analytics,security-headers
|
- traefik.http.routers.app.middlewares=security-headers
|
||||||
|
|
||||||
# Admin interface with authentication
|
# Admin interface with authentication
|
||||||
- traefik.http.routers.admin.middlewares=basic-auth,security-headers
|
- traefik.http.routers.admin.middlewares=authelia@docker,security-headers
|
||||||
|
|
||||||
# API endpoint (security headers only)
|
# API endpoint (security headers only)
|
||||||
- traefik.http.routers.api.middlewares=security-headers
|
- traefik.http.routers.api.middlewares=security-headers
|
||||||
@@ -274,6 +305,7 @@ services:
|
|||||||
|
|
||||||
### Built-in Monitoring
|
### Built-in Monitoring
|
||||||
- **Uptime Kuma**: Service availability monitoring
|
- **Uptime Kuma**: Service availability monitoring
|
||||||
|
- **Beszel**: Server resource monitoring (CPU, RAM, disk, network)
|
||||||
- **Traefik Dashboard**: Traffic and routing metrics
|
- **Traefik Dashboard**: Traffic and routing metrics
|
||||||
- **Umami Analytics**: Web traffic analytics (privacy-focused)
|
- **Umami Analytics**: Web traffic analytics (privacy-focused)
|
||||||
|
|
||||||
|
|||||||
61
authelia/configuration.template.yml
Normal file
61
authelia/configuration.template.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
# Authelia configuration
|
||||||
|
# This is a minimal configuration for getting started with Authelia
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: 'tcp://:9091'
|
||||||
|
endpoints:
|
||||||
|
authz:
|
||||||
|
forward-auth:
|
||||||
|
implementation: 'ForwardAuth'
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: '/config/users_database.yml'
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: 'one_factor'
|
||||||
|
rules:
|
||||||
|
- domain: 'pgadmin.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'beszel.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'traefik.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: '*.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
policy: 'one_factor'
|
||||||
|
|
||||||
|
session:
|
||||||
|
name: 'authelia_session'
|
||||||
|
cookies:
|
||||||
|
- domain: '${DOMAIN}'
|
||||||
|
authelia_url: 'https://auth.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
default_redirection_url: 'https://portainer.${DOMAIN_PREFIX}.${DOMAIN}'
|
||||||
|
|
||||||
|
storage:
|
||||||
|
postgres:
|
||||||
|
address: 'tcp://authelia-db:5432'
|
||||||
|
database: '${AUTHELIA_DB_NAME}'
|
||||||
|
username: '${AUTHELIA_DB_USER}'
|
||||||
|
password: '${AUTHELIA_DB_PASSWORD}'
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
disable_startup_check: true
|
||||||
|
# Configure SMTP for production email notifications
|
||||||
|
# For testing, you can use filesystem notifier instead:
|
||||||
|
# filesystem:
|
||||||
|
# filename: /config/notification.txt
|
||||||
|
smtp:
|
||||||
|
address: 'submissions://smtp.gmail.com:465'
|
||||||
|
username: 'your-email@gmail.com'
|
||||||
|
sender: 'Authelia <noreply@${DOMAIN}>'
|
||||||
|
# For Gmail, use an App Password (not your regular password)
|
||||||
|
# Generate at: https://myaccount.google.com/apppasswords
|
||||||
|
|
||||||
|
identity_validation:
|
||||||
|
reset_password: {}
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 120
|
||||||
|
ban_time: 300
|
||||||
61
authelia/configuration.yml
Normal file
61
authelia/configuration.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
# Authelia configuration
|
||||||
|
# This is a minimal configuration for getting started with Authelia
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: 'tcp://:9091'
|
||||||
|
endpoints:
|
||||||
|
authz:
|
||||||
|
forward-auth:
|
||||||
|
implementation: 'ForwardAuth'
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: '/config/users_database.yml'
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: 'one_factor'
|
||||||
|
rules:
|
||||||
|
- domain: 'pgadmin.test.3launchpad.com'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'beszel.test.3launchpad.com'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: 'traefik.test.3launchpad.com'
|
||||||
|
policy: 'two_factor'
|
||||||
|
- domain: '*.test.3launchpad.com'
|
||||||
|
policy: 'one_factor'
|
||||||
|
|
||||||
|
session:
|
||||||
|
name: 'authelia_session'
|
||||||
|
cookies:
|
||||||
|
- domain: '3launchpad.com'
|
||||||
|
authelia_url: 'https://auth.test.3launchpad.com'
|
||||||
|
default_redirection_url: 'https://portainer.test.3launchpad.com'
|
||||||
|
|
||||||
|
storage:
|
||||||
|
postgres:
|
||||||
|
address: 'tcp://authelia-db:5432'
|
||||||
|
database: 'authelia'
|
||||||
|
username: 'authelia'
|
||||||
|
password: 'p/6EGIgTxPndniwUCY54G7q5jOPqXofF'
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
disable_startup_check: true
|
||||||
|
# Configure SMTP for production email notifications
|
||||||
|
# For testing, you can use filesystem notifier instead:
|
||||||
|
# filesystem:
|
||||||
|
# filename: /config/notification.txt
|
||||||
|
smtp:
|
||||||
|
address: 'submissions://smtp.gmail.com:465'
|
||||||
|
username: 'your-email@gmail.com'
|
||||||
|
sender: 'Authelia <noreply@3launchpad.com>'
|
||||||
|
# For Gmail, use an App Password (not your regular password)
|
||||||
|
# Generate at: https://myaccount.google.com/apppasswords
|
||||||
|
|
||||||
|
identity_validation:
|
||||||
|
reset_password: {}
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 120
|
||||||
|
ban_time: 300
|
||||||
17
authelia/users_database.yml
Normal file
17
authelia/users_database.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
# Authelia users database
|
||||||
|
# This is a simple file-based user database
|
||||||
|
#
|
||||||
|
# To generate a new password hash, run:
|
||||||
|
# docker run --rm authelia/authelia:latest authelia crypto hash generate argon2 --password 'YOUR_PASSWORD'
|
||||||
|
#
|
||||||
|
# IMPORTANT: Change the default password before production use!
|
||||||
|
|
||||||
|
users:
|
||||||
|
admin:
|
||||||
|
displayname: "Administrator"
|
||||||
|
# Default password: "Admin@123456" - CHANGE THIS!
|
||||||
|
password: "$argon2id$v=19$m=65536,t=3,p=4$O1Qjq7AB4/xJ7Qk1dUqp/g$PhVqFWEqyQTJeSnCeiCC3lrcWcpw37kYttw4Xh/qUsk"
|
||||||
|
email: admin@3launchpad.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
1
beszel_agent_data/fingerprint
Normal file
1
beszel_agent_data/fingerprint
Normal file
@@ -0,0 +1 @@
|
|||||||
|
c17e1b1086d55cc7d4bfacbfa6b9472ca975863413fb629f
|
||||||
@@ -12,6 +12,11 @@ volumes:
|
|||||||
uptime_kuma_data:
|
uptime_kuma_data:
|
||||||
umami_data:
|
umami_data:
|
||||||
pgadmin_data:
|
pgadmin_data:
|
||||||
|
authelia_db_data:
|
||||||
|
beszel_data:
|
||||||
|
gitea_data:
|
||||||
|
gitea_db_data:
|
||||||
|
duplicati_config:
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# Services
|
# Services
|
||||||
@@ -22,7 +27,7 @@ services:
|
|||||||
## Traefik — edge router + ACME (HTTP-01)
|
## Traefik — edge router + ACME (HTTP-01)
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
traefik:
|
traefik:
|
||||||
image: traefik:v3.1
|
image: traefik:latest
|
||||||
container_name: traefik
|
container_name: traefik
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
@@ -41,6 +46,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
TZ: "${TZ}"
|
TZ: "${TZ}"
|
||||||
command:
|
command:
|
||||||
|
# Enable ping endpoint for health checks
|
||||||
|
- --ping=true
|
||||||
|
|
||||||
# Experimental plugins
|
# Experimental plugins
|
||||||
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
- --experimental.plugins.traefik-umami-plugin.modulename=github.com/1cedsoda/traefik-umami-plugin
|
||||||
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
- --experimental.plugins.traefik-umami-plugin.version=v1.0.3
|
||||||
@@ -88,42 +96,98 @@ services:
|
|||||||
- --accesslog.filepath=/var/log/traefik/access.log
|
- --accesslog.filepath=/var/log/traefik/access.log
|
||||||
- --accesslog.bufferingsize=100
|
- --accesslog.bufferingsize=100
|
||||||
- --log.level=INFO
|
- --log.level=INFO
|
||||||
|
- --metrics.prometheus=true
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/rundocker.sock:ro
|
||||||
- traefik_letsencrypt:/letsencrypt
|
- traefik_letsencrypt:/letsencrypt
|
||||||
- traefik_logs:/var/log/traefik
|
- traefik_logs:/var/log/traefik
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
|
|
||||||
# Reusable security headers
|
# Reusable security headers middleware
|
||||||
- traefik.http.middlewares.security-headers.headers.stsSeconds=31536000
|
- "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
|
||||||
- traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true
|
- "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
|
||||||
- traefik.http.middlewares.security-headers.headers.stsPreload=true
|
- "traefik.http.middlewares.security-headers.headers.stsPreload=true"
|
||||||
- traefik.http.middlewares.security-headers.headers.browserXssFilter=true
|
- "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
|
||||||
- traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true
|
- "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
|
||||||
- traefik.http.middlewares.security-headers.headers.referrerPolicy=no-referrer-when-downgrade
|
- "traefik.http.middlewares.security-headers.headers.referrerPolicy=no-referrer-when-downgrade"
|
||||||
- traefik.http.middlewares.security-headers.headers.frameDeny=true
|
|
||||||
|
|
||||||
# Basic Auth middleware
|
# Authelia middleware
|
||||||
- traefik.http.middlewares.basic-auth.basicauth.users=${BASIC_AUTH_USERS}
|
- "traefik.http.middlewares.authelia.forwardAuth.address=http://authelia:9091/api/authz/forward-auth"
|
||||||
|
- "traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true"
|
||||||
# Umami Analytics middleware (commented out until real website ID is available)
|
- "traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Email,Remote-Name"
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.umamiHost=umami:3000
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.websiteId=${UMAMI_WEBSITE_ID}
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.forwardPath=umami
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.scriptInjection=true
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.scriptInjectionMode=tag
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.autoTrack=true
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.doNotTrack=false
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.cache=false
|
|
||||||
# - traefik.http.middlewares.umami-analytics.plugin.traefik-umami-plugin.serverSideTracking=false
|
|
||||||
|
|
||||||
# Traefik dashboard (protected)
|
# Traefik dashboard (protected)
|
||||||
- traefik.http.routers.traefik.rule=Host(`traefik.gate.${DOMAIN}`)
|
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
- traefik.http.routers.traefik.entrypoints=websecure
|
- "traefik.http.routers.traefik.entrypoints=websecure"
|
||||||
- traefik.http.routers.traefik.tls.certresolver=le
|
- "traefik.http.routers.traefik.tls.certresolver=le"
|
||||||
- traefik.http.routers.traefik.service=api@internal
|
- "traefik.http.routers.traefik.service=api@internal"
|
||||||
- traefik.http.routers.traefik.middlewares=basic-auth,security-headers
|
- "traefik.http.routers.traefik.middlewares=authelia@docker,security-headers@docker"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/ping"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Authelia — authentication and authorization
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
authelia:
|
||||||
|
image: authelia/authelia:latest
|
||||||
|
container_name: authelia
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
volumes:
|
||||||
|
- ./authelia:/config
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
eval "echo \"$(cat /config/configuration.template.yml)\"" > /config/configuration.yml
|
||||||
|
exec /app/entrypoint.sh
|
||||||
|
environment:
|
||||||
|
TZ: "${TZ}"
|
||||||
|
DOMAIN: "${DOMAIN}"
|
||||||
|
DOMAIN_PREFIX: "${DOMAIN_PREFIX}"
|
||||||
|
AUTHELIA_DB_NAME: "${AUTHELIA_DB_NAME}"
|
||||||
|
AUTHELIA_DB_USER: "${AUTHELIA_DB_USER}"
|
||||||
|
AUTHELIA_DB_PASSWORD: "${AUTHELIA_DB_PASSWORD}"
|
||||||
|
AUTHELIA_SESSION_SECRET: '${AUTHELIA_SESSION_SECRET}'
|
||||||
|
AUTHELIA_STORAGE_ENCRYPTION_KEY: '${AUTHELIA_STORAGE_ENCRYPTION_KEY}'
|
||||||
|
AUTHELIA_STORAGE_POSTGRES_PASSWORD: '${AUTHELIA_DB_PASSWORD}'
|
||||||
|
AUTHELIA_NOTIFIER_SMTP_PASSWORD: '${AUTHELIA_NOTIFIER_SMTP_PASSWORD}'
|
||||||
|
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET: '${AUTHELIA_JWT_SECRET}'
|
||||||
|
depends_on:
|
||||||
|
- authelia-db
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.authelia.rule=Host(`auth.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.authelia.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.authelia.tls.certresolver=le"
|
||||||
|
- "traefik.http.routers.authelia.middlewares=security-headers@docker"
|
||||||
|
- "traefik.http.services.authelia.loadbalancer.server.port=9091"
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Authelia Database — PostgreSQL
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
authelia-db:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: authelia-db
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${AUTHELIA_DB_NAME}
|
||||||
|
POSTGRES_USER: ${AUTHELIA_DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${AUTHELIA_DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- authelia_db_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U authelia -d authelia"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Portainer — Docker control plane
|
## Portainer — Docker control plane
|
||||||
@@ -133,34 +197,35 @@ services:
|
|||||||
container_name: portainer
|
container_name: portainer
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
|
command: --host unix:///var/run/docker.sock
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- portainer_data:/data
|
- portainer_data:/data
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
- traefik.http.routers.portainer.rule=Host(`portainer.gate.${DOMAIN}`)
|
- "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
- traefik.http.routers.portainer.entrypoints=websecure
|
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||||
- traefik.http.routers.portainer.tls.certresolver=le
|
- "traefik.http.routers.portainer.tls.certresolver=le"
|
||||||
- traefik.http.routers.portainer.middlewares=security-headers
|
- "traefik.http.routers.portainer.middlewares=security-headers@docker"
|
||||||
- traefik.http.services.portainer.loadbalancer.server.port=9000
|
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Uptime Kuma — status page / checks
|
## Uptime Kuma — status page / checks
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
uptime-kuma:
|
uptime-kuma:
|
||||||
image: louislam/uptime-kuma:Latest
|
image: louislam/uptime-kuma:latest
|
||||||
container_name: uptime-kuma
|
container_name: uptime-kuma
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- uptime_kuma_data:/app/data
|
- uptime_kuma_data:/app/data
|
||||||
networks: [traefik_proxy]
|
networks: [traefik_proxy]
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
- traefik.http.routers.kuma.rule=Host(`uptime.gate.${DOMAIN}`)
|
- "traefik.http.routers.kuma.rule=Host(`uptime.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
- traefik.http.routers.kuma.entrypoints=websecure
|
- "traefik.http.routers.kuma.entrypoints=websecure"
|
||||||
- traefik.http.routers.kuma.tls.certresolver=le
|
- "traefik.http.routers.kuma.tls.certresolver=le"
|
||||||
- traefik.http.routers.kuma.middlewares=basic-auth,security-headers
|
- "traefik.http.routers.kuma.middlewares=security-headers@docker"
|
||||||
- traefik.http.services.kuma.loadbalancer.server.port=3001
|
- "traefik.http.services.kuma.loadbalancer.server.port=3001"
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Umami — web analytics
|
## Umami — web analytics
|
||||||
@@ -177,12 +242,18 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- umami-db
|
- umami-db
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
- traefik.http.routers.umami.rule=Host(`umami.gate.${DOMAIN}`)
|
- "traefik.http.routers.umami.rule=Host(`umami.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
- traefik.http.routers.umami.entrypoints=websecure
|
- "traefik.http.routers.umami.entrypoints=websecure"
|
||||||
- traefik.http.routers.umami.tls.certresolver=le
|
- "traefik.http.routers.umami.tls.certresolver=le"
|
||||||
- traefik.http.routers.umami.middlewares=security-headers
|
- "traefik.http.routers.umami.middlewares=security-headers@docker"
|
||||||
- traefik.http.services.umami.loadbalancer.server.port=3000
|
- "traefik.http.services.umami.loadbalancer.server.port=3000"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
## ─────────────────────────────────────────────
|
## ─────────────────────────────────────────────
|
||||||
## Umami Database — PostgreSQL
|
## Umami Database — PostgreSQL
|
||||||
@@ -198,6 +269,12 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
POSTGRES_PASSWORD: ${UMAMI_DB_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- umami_data:/var/lib/postgresql/data
|
- umami_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${UMAMI_DB_USER} -d ${UMAMI_DB_NAME}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
# ─────────────────────────────────────────────
|
# ─────────────────────────────────────────────
|
||||||
# pgAdmin — PostgreSQL administration
|
# pgAdmin — PostgreSQL administration
|
||||||
@@ -212,18 +289,141 @@ services:
|
|||||||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
||||||
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
PGADMIN_CONFIG_SERVER_MODE: 'True'
|
||||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
||||||
# Fix CSRF issues behind reverse proxy
|
|
||||||
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
PGADMIN_CONFIG_WTF_CSRF_CHECK_DEFAULT: 'False'
|
||||||
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
PGADMIN_CONFIG_WTF_CSRF_TIME_LIMIT: 'None'
|
||||||
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION: 'False'
|
||||||
# Trust proxy headers
|
|
||||||
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
PGADMIN_CONFIG_PROXY_X_HOST_COUNT: '1'
|
||||||
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: '1'
|
||||||
volumes:
|
volumes:
|
||||||
- pgadmin_data:/var/lib/pgadmin
|
- pgadmin_data:/var/lib/pgadmin
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- "traefik.enable=true"
|
||||||
- traefik.http.routers.pgadmin.rule=Host(`pgadmin.gate.${DOMAIN}`)
|
- "traefik.http.routers.pgadmin.rule=Host(`pgadmin.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
- traefik.http.routers.pgadmin.entrypoints=websecure
|
- "traefik.http.routers.pgadmin.entrypoints=websecure"
|
||||||
- traefik.http.routers.pgadmin.tls.certresolver=le
|
- "traefik.http.routers.pgadmin.tls.certresolver=le"
|
||||||
- traefik.http.services.pgadmin.loadbalancer.server.port=80
|
- "traefik.http.routers.pgadmin.middlewares=security-headers@docker"
|
||||||
|
- "traefik.http.services.pgadmin.loadbalancer.server.port=80"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Beszel Hub — lightweight server monitoring
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
beszel:
|
||||||
|
image: henrygd/beszel:latest
|
||||||
|
container_name: beszel
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
volumes:
|
||||||
|
- beszel_data:/beszel_data
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.beszel.rule=Host(`beszel.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.beszel.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.beszel.tls.certresolver=le"
|
||||||
|
- "traefik.http.routers.beszel.middlewares=security-headers@docker"
|
||||||
|
- "traefik.http.services.beszel.loadbalancer.server.port=8090"
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Gitea — self-hosted Git service
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
gitea:
|
||||||
|
image: docker.gitea.com/gitea:latest
|
||||||
|
container_name: gitea
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
environment:
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__database__DB_TYPE=postgres
|
||||||
|
- GITEA__database__HOST=gitea-db:5432
|
||||||
|
- GITEA__database__NAME=${GITEA_DB_NAME}
|
||||||
|
- GITEA__database__USER=${GITEA_DB_USER}
|
||||||
|
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
|
||||||
|
- GITEA__database__SSL_MODE=disable
|
||||||
|
- GITEA__server__DOMAIN=git.${DOMAIN_PREFIX}.${DOMAIN}
|
||||||
|
- GITEA__server__SSH_DOMAIN=git.${DOMAIN_PREFIX}.${DOMAIN}
|
||||||
|
- GITEA__server__ROOT_URL=https://git.${DOMAIN_PREFIX}.${DOMAIN}/
|
||||||
|
- GITEA__server__SSH_PORT=222
|
||||||
|
- GITEA__server__SSH_LISTEN_PORT=22
|
||||||
|
- GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY}
|
||||||
|
- GITEA__security__INTERNAL_TOKEN=${GITEA_INTERNAL_TOKEN}
|
||||||
|
- GITEA__i18n__LANGS=en-US
|
||||||
|
- GITEA__i18n__NAMES=English
|
||||||
|
volumes:
|
||||||
|
- gitea_data:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "222:22"
|
||||||
|
depends_on:
|
||||||
|
- gitea-db
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.gitea.rule=Host(`git.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.gitea.tls.certresolver=le"
|
||||||
|
- "traefik.http.routers.gitea.middlewares=security-headers@docker,authelia@docker"
|
||||||
|
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Gitea Database — PostgreSQL
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
gitea-db:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
container_name: gitea-db
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${GITEA_DB_NAME}
|
||||||
|
POSTGRES_USER: ${GITEA_DB_USER}
|
||||||
|
POSTGRES_PASSWORD: ${GITEA_DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- gitea_db_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${GITEA_DB_USER} -d ${GITEA_DB_NAME}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
## Duplicati — encrypted cloud backup
|
||||||
|
## ─────────────────────────────────────────────
|
||||||
|
duplicati:
|
||||||
|
image: lscr.io/linuxserver/duplicati:latest
|
||||||
|
container_name: duplicati
|
||||||
|
restart: unless-stopped
|
||||||
|
networks: [traefik_proxy]
|
||||||
|
environment:
|
||||||
|
- PUID=0
|
||||||
|
- PGID=0
|
||||||
|
- TZ=${TZ}
|
||||||
|
- SETTINGS_ENCRYPTION_KEY=${DUPLICATI_ENCRYPTION_KEY}
|
||||||
|
- CLI_ARGS=--webservice-allowed-hostnames=* --webservice-password=${DUPLICATI_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- duplicati_config:/config
|
||||||
|
- /:/source:ro
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.duplicati.rule=Host(`backup.${DOMAIN_PREFIX}.${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.duplicati.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.duplicati.tls.certresolver=le"
|
||||||
|
- "traefik.http.routers.duplicati.middlewares=security-headers@docker"
|
||||||
|
- "traefik.http.services.duplicati.loadbalancer.server.port=8200"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8200"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|||||||
Reference in New Issue
Block a user