feat: migrate Outline + n8n to main server, rename S3 buckets to walava-*

- Add Outline, outline-db, outline-redis, n8n, outline-mcp containers to main docker-compose
- Add env.outline.j2 template with Resend SMTP and S3 (walava-outline bucket)
- Update Traefik routes: wiki → outline:3000, auto → n8n:5678 (local, not cross-server)
- Rename S3 buckets: visual-backup → walava-backup, visual-outline → walava-outline
- Extend backup.sh.j2: add Outline DB, n8n, Plane MinIO to backup scope
- Add outline_image, n8n_image, outline_mcp_image to services/defaults
- Remove Authelia config deployment tasks from configs.yml
- Add outline-internal and n8n-internal networks to docker-compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jack 2026-03-27 03:04:54 +07:00
parent fba7eb68ea
commit 489791403c
7 changed files with 224 additions and 24 deletions

View file

@ -5,4 +5,4 @@ backup_user: deploy
# Timeweb S3 offsite backups # Timeweb S3 offsite backups
s3_endpoint: "https://s3.timeweb.cloud" s3_endpoint: "https://s3.timeweb.cloud"
s3_bucket: "visual-backup" s3_bucket: "walava-backup"

View file

@ -32,6 +32,12 @@ docker exec plane-db pg_dump -U plane plane \
| gzip > "${WORK_DIR}/data/databases/plane.sql.gz" | gzip > "${WORK_DIR}/data/databases/plane.sql.gz"
log " → databases/plane.sql.gz ($(du -sh "${WORK_DIR}/data/databases/plane.sql.gz" | cut -f1))" log " → databases/plane.sql.gz ($(du -sh "${WORK_DIR}/data/databases/plane.sql.gz" | cut -f1))"
# ── PostgreSQL: Outline ──────────────────────────────────────────────────────
log "Dumping outline-db..."
docker exec outline-db pg_dump -U outline outline \
| gzip > "${WORK_DIR}/data/databases/outline.sql.gz"
log " → databases/outline.sql.gz ($(du -sh "${WORK_DIR}/data/databases/outline.sql.gz" | cut -f1))"
# ── Forgejo data volume (repos, attachments, LFS) ─────────────────────────── # ── Forgejo data volume (repos, attachments, LFS) ───────────────────────────
log "Backing up Forgejo data..." log "Backing up Forgejo data..."
docker run --rm \ docker run --rm \
@ -50,6 +56,24 @@ docker run --rm \
tar czf /backup/uptime-kuma.tar.gz /app/data tar czf /backup/uptime-kuma.tar.gz /app/data
log " → volumes/uptime-kuma.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/uptime-kuma.tar.gz" | cut -f1))" log " → volumes/uptime-kuma.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/uptime-kuma.tar.gz" | cut -f1))"
# ── n8n workflows + credentials ──────────────────────────────────────────────
log "Backing up n8n..."
docker run --rm \
--volumes-from n8n \
-v "${WORK_DIR}/data/volumes:/backup" \
alpine:3 \
tar czf /backup/n8n.tar.gz /home/node/.n8n
log " → volumes/n8n.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/n8n.tar.gz" | cut -f1))"
# ── Plane MinIO (uploaded files / attachments) ───────────────────────────────
log "Backing up Plane MinIO..."
docker run --rm \
--volumes-from plane-minio \
-v "${WORK_DIR}/data/volumes:/backup" \
alpine:3 \
tar czf /backup/plane-minio.tar.gz /data
log " → volumes/plane-minio.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/plane-minio.tar.gz" | cut -f1))"
# ── Add restore instructions ───────────────────────────────────────────────── # ── Add restore instructions ─────────────────────────────────────────────────
cat > "${WORK_DIR}/data/RESTORE.md" << 'RESTORE_EOF' cat > "${WORK_DIR}/data/RESTORE.md" << 'RESTORE_EOF'
# Restore Instructions # Restore Instructions
@ -72,6 +96,9 @@ zcat data/databases/forgejo.sql.gz | docker exec -i forgejo-db psql -U forgejo f
# Plane DB # Plane DB
zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane
# Outline DB
zcat data/databases/outline.sql.gz | docker exec -i outline-db psql -U outline outline
``` ```
## Step 3 — Restore volume data ## Step 3 — Restore volume data
@ -80,9 +107,17 @@ zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane
docker run --rm --volumes-from forgejo -v $(pwd)/data/volumes:/backup \ docker run --rm --volumes-from forgejo -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/forgejo.tar.gz" alpine:3 sh -c "cd / && tar xzf /backup/forgejo.tar.gz"
# Uptime Kuma — extracts /app/data/ into the container # Uptime Kuma
docker run --rm --volumes-from uptime-kuma -v $(pwd)/data/volumes:/backup \ docker run --rm --volumes-from uptime-kuma -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/uptime-kuma.tar.gz" alpine:3 sh -c "cd / && tar xzf /backup/uptime-kuma.tar.gz"
# n8n
docker run --rm --volumes-from n8n -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/n8n.tar.gz"
# Plane MinIO (uploaded files)
docker run --rm --volumes-from plane-minio -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/plane-minio.tar.gz"
``` ```
## Step 4 — Restart services ## Step 4 — Restart services

View file

@ -28,3 +28,8 @@ promtail_image: "grafana/promtail:3.4.3" # https://hub
crowdsec_image: "crowdsecurity/crowdsec:v1.6.8" # https://hub.docker.com/r/crowdsecurity/crowdsec/tags crowdsec_image: "crowdsecurity/crowdsec:v1.6.8" # https://hub.docker.com/r/crowdsecurity/crowdsec/tags
redis_image: "redis:7-alpine" redis_image: "redis:7-alpine"
uptime_kuma_image: "louislam/uptime-kuma:1" # https://hub.docker.com/r/louislam/uptime-kuma/tags uptime_kuma_image: "louislam/uptime-kuma:1" # https://hub.docker.com/r/louislam/uptime-kuma/tags
outline_image: "outlinewiki/outline:0.80.2" # https://hub.docker.com/r/outlinewiki/outline/tags
outline_db_image: "postgres:15-alpine"
outline_redis_image: "redis:7-alpine"
n8n_image: "n8nio/n8n:1.89.2" # https://hub.docker.com/r/n8nio/n8n/tags
outline_mcp_image: "git.{{ domain_base }}/jack/outline-mcp:latest"

View file

@ -8,6 +8,15 @@
mode: "0600" mode: "0600"
notify: Restart stack notify: Restart stack
- name: Deploy Outline .env file
ansible.builtin.template:
src: env.outline.j2
dest: "{{ services_root }}/.env.outline"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy docker-compose.yml - name: Deploy docker-compose.yml
ansible.builtin.template: ansible.builtin.template:
src: docker-compose.yml.j2 src: docker-compose.yml.j2
@ -143,24 +152,6 @@
mode: "0644" mode: "0644"
notify: Restart stack notify: Restart stack
- name: Deploy Authelia configuration
ansible.builtin.template:
src: authelia/configuration.yml.j2
dest: "{{ services_root }}/authelia/configuration.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy Authelia users database
ansible.builtin.template:
src: authelia/users.yml.j2
dest: "{{ services_root }}/authelia/users.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy Traefik logrotate config - name: Deploy Traefik logrotate config
ansible.builtin.template: ansible.builtin.template:
src: logrotate/traefik.j2 src: logrotate/traefik.j2

View file

@ -26,6 +26,12 @@ networks:
monitoring: monitoring:
driver: bridge driver: bridge
internal: true internal: true
outline-internal:
driver: bridge
internal: true
n8n-internal:
driver: bridge
internal: true
volumes: volumes:
forgejo_data: forgejo_data:
forgejo_db_data: forgejo_db_data:
@ -39,6 +45,9 @@ volumes:
loki_data: loki_data:
crowdsec_data: crowdsec_data:
uptime_kuma_data: uptime_kuma_data:
outline_db_data:
outline_redis_data:
n8n_data:
services: services:
@ -595,3 +604,123 @@ services:
options: options:
max-size: "5m" max-size: "5m"
max-file: "2" max-file: "2"
# ── Outline wiki ────────────────────────────────────────────────────────────
outline:
image: {{ outline_image }}
container_name: outline
restart: unless-stopped
env_file: .env.outline
networks:
- outline-internal
- backend
depends_on:
outline-db:
condition: service_healthy
outline-redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/_health"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
outline-db:
image: {{ outline_db_image }}
container_name: outline-db
restart: unless-stopped
environment:
POSTGRES_DB: outline
POSTGRES_USER: outline
POSTGRES_PASSWORD: {{ outline_db_password }}
networks:
- outline-internal
volumes:
- outline_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
outline-redis:
image: {{ outline_redis_image }}
container_name: outline-redis
restart: unless-stopped
networks:
- outline-internal
volumes:
- outline_redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── n8n workflow automation ──────────────────────────────────────────────────
n8n:
image: {{ n8n_image }}
container_name: n8n
restart: unless-stopped
networks:
- n8n-internal
- backend
volumes:
- n8n_data:/home/node/.n8n
environment:
- N8N_HOST={{ domain_n8n }}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://{{ domain_n8n }}/
- N8N_ENCRYPTION_KEY={{ n8n_encryption_key }}
- N8N_USER_MANAGEMENT_JWT_SECRET={{ n8n_jwt_secret }}
- GENERIC_TIMEZONE=Europe/Moscow
- TZ=Europe/Moscow
- N8N_METRICS=false
- N8N_LOG_LEVEL=warn
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=336
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/healthz"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Outline MCP server ───────────────────────────────────────────────────────
outline-mcp:
image: {{ outline_mcp_image }}
container_name: outline-mcp
restart: unless-stopped
networks:
- backend
environment:
- OUTLINE_URL=https://{{ domain_wiki }}
- OUTLINE_API_KEY={{ outline_mcp_api_key }}
- PORT=8765
- HOST=0.0.0.0
- LOG_LEVEL=INFO
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

View file

@ -0,0 +1,42 @@
# Outline env — generated by Ansible
NODE_ENV=production
SECRET_KEY={{ outline_secret_key }}
UTILS_SECRET={{ outline_utils_secret }}
# Database
DATABASE_URL=postgres://outline:{{ outline_db_password }}@outline-db:5432/outline
PGSSLMODE=disable
# Redis
REDIS_URL=redis://outline-redis:6379
# App URL
URL=https://{{ domain_wiki }}
PORT=3000
# S3 file storage (Timeweb Object Storage)
AWS_ACCESS_KEY_ID={{ s3_access_key }}
AWS_SECRET_ACCESS_KEY={{ s3_secret_key }}
AWS_REGION=ru-1
AWS_S3_UPLOAD_BUCKET_NAME=walava-outline
AWS_S3_UPLOAD_BUCKET_URL=https://s3.timeweb.cloud
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
FILE_STORAGE=s3
# Auth
AUTH_PROVIDERS=email
# SMTP via Resend (direct — main server has outbound SMTP)
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USERNAME=resend
SMTP_PASSWORD={{ resend_api_key }}
SMTP_FROM_EMAIL=noreply@{{ domain_base }}
SMTP_FROM_NAME=Visual Wiki
SMTP_SECURE=false
# Optional
DEFAULT_LANGUAGE=en_US
RATE_LIMITER_ENABLED=true
ENABLE_UPDATES=false

View file

@ -89,7 +89,6 @@ http:
service: walava-landing service: walava-landing
middlewares: [rate-limit-default] middlewares: [rate-limit-default]
# ── Cross-server: tools ({{ ip_tools }}) ─────────────────────────────────
wiki: wiki:
rule: "Host(`{{ domain_wiki }}`)" rule: "Host(`{{ domain_wiki }}`)"
entrypoints: [websecure] entrypoints: [websecure]
@ -147,16 +146,15 @@ http:
servers: servers:
- url: "http://walava-web:80" - url: "http://walava-web:80"
# ── Cross-server services ─────────────────────────────────────────────────
wiki: wiki:
loadBalancer: loadBalancer:
servers: servers:
- url: "http://{{ ip_tools }}:3000" - url: "http://outline:3000"
n8n: n8n:
loadBalancer: loadBalancer:
servers: servers:
- url: "http://{{ ip_tools }}:5678" - url: "http://n8n:5678"
middlewares: middlewares:
# ── Security Headers (applied globally via entrypoint) ───────────────── # ── Security Headers (applied globally via entrypoint) ─────────────────