8 Commits

Author SHA1 Message Date
Eduardo David Paredes Vara
81c7b45069 mail server n8n 2026-03-20 23:39:52 +00:00
Eduardo David Paredes Vara
d484dd5e5f mail server paperless 2026-03-20 23:30:44 +00:00
Eduardo David Paredes Vara
8bd6b3b9f2 nextcloud mail 2026-03-20 23:07:26 +00:00
Eduardo David Paredes Vara
24270af2f0 Mail 2026-03-20 22:35:42 +00:00
Eduardo David Paredes Vara
1f7ed5071d stacks env 2026-03-17 15:30:01 +00:00
Eduardo David Paredes Vara
59cc0c0bab certificados 2026-03-17 00:00:33 +00:00
Eduardo David Paredes Vara
411ebf968d certificados 2026-03-16 23:55:46 +00:00
Eduardo David Paredes Vara
8ff1456a3b certificados 2026-03-16 23:47:33 +00:00
10 changed files with 652 additions and 6 deletions

137
mail-relay/README.md Normal file
View File

@@ -0,0 +1,137 @@
# Mail Relay - SMTP de salida para stacks
Stack de relay SMTP interno para centralizar el envio de correos de tus apps sin montar un servidor de correo completo.
## 📋 Descripcion
Este stack despliega un relay SMTP con Postfix usando `boky/postfix` para:
- Recibir correo SMTP desde contenedores internos
- Reenviar todo a un proveedor SMTP externo (smarthost)
- Firmar con DKIM (autogenerado)
- Unificar configuracion de correo para todos los stacks
No incluye recepcion de correo (MX, IMAP, POP3, webmail).
## 🚀 Despliegue
### 1. Preparar rutas y secreto en el host
```bash
sudo mkdir -p /opt/mail-relay/{queue,opendkim,secrets}
sudo chmod 700 /opt/mail-relay/secrets
printf '%s' 'CAMBIA_ESTA_PASSWORD_SMTP' | sudo tee /opt/mail-relay/secrets/relayhost_password > /dev/null
sudo chmod 600 /opt/mail-relay/secrets/relayhost_password
```
### 2. Desplegar desde Portainer
1. Ve a **Stacks** -> **Add stack**
2. Nombre: `mail-relay`
3. Configura el repositorio Git
4. Compose path: `mail-relay/docker-compose.yml`
5. Carga variables desde `mail-relay/stack.env`
6. Ajusta al menos:
- `MAIL_RELAY_HOSTNAME`
- `MAIL_RELAY_ALLOWED_SENDER_DOMAINS`
- `MAIL_RELAY_MASQUERADED_DOMAINS`
- `MAIL_RELAY_SMARTHOST`
- `MAIL_RELAY_SMARTHOST_USERNAME`
7. Deploy del stack
## ⚙️ Variables importantes
```env
MAIL_RELAY_SMARTHOST=[smtp.proveedor.tld]:587
MAIL_RELAY_SMARTHOST_USERNAME=usuario-smtp
MAIL_RELAY_SMTP_TLS_SECURITY_LEVEL=encrypt
MAIL_RELAY_ALLOWED_SENDER_DOMAINS=tudominio.com
MAIL_RELAY_MASQUERADED_DOMAINS=tudominio.com
```
La password SMTP no se pone en `stack.env`; se lee desde el archivo host:
```text
/opt/mail-relay/secrets/relayhost_password
```
## 🔌 Conectar otras apps
En cada stack que deba enviar correo, añade la red externa:
```yaml
networks:
mail_internal:
external: true
```
Y en el servicio de la app:
```yaml
services:
tu-app:
networks:
- default
- mail_internal
```
Config SMTP en la app:
```env
SMTP_HOST=mail-relay
SMTP_PORT=587
SMTP_FROM=noreply@tudominio.com
```
## ✅ DNS minimo recomendado
Para buena entregabilidad en Gmail/Outlook:
- SPF
- DKIM
- DMARC
### SPF (ejemplo)
```text
v=spf1 include:spf.tu-proveedor.tld ~all
```
### DKIM
Tras el primer arranque, extrae el TXT generado:
```bash
find /opt/mail-relay/opendkim -type f -name '*.txt' -exec echo '### {}' \; -exec cat {} \;
```
Copia esos valores a tu DNS.
### DMARC (inicio en monitorizacion)
```text
v=DMARC1; p=none; adkim=s; aspf=s
```
## 🧪 Prueba rapida
```bash
docker exec -i mail-relay sendmail -t <<'EOF'
From: noreply@tudominio.com
To: destino@example.com
Subject: prueba mail relay
Correo de prueba del stack mail-relay.
EOF
```
## 🛠️ Troubleshooting
Ver logs:
```bash
docker logs -f mail-relay
```
Errores tipicos:
- `Relay access denied`: revisa `MAIL_RELAY_ALLOWED_SENDER_DOMAINS`
- Auth fallida con proveedor: revisa usuario/password SMTP
- Rechazo por DNS: valida SPF/DKIM/DMARC

View File

@@ -0,0 +1,52 @@
services:
mail-relay:
image: ${MAIL_RELAY_IMAGE}
container_name: mail-relay
restart: unless-stopped
environment:
TZ: ${TZ}
LOG_FORMAT: ${MAIL_RELAY_LOG_FORMAT}
# Hostname del relay
POSTFIX_myhostname: ${MAIL_RELAY_HOSTNAME}
# Solo clientes internos del stack de correo
POSTFIX_mynetworks: ${MAIL_RELAY_MYNETWORKS}
# Dominios permitidos para el sender
ALLOWED_SENDER_DOMAINS: ${MAIL_RELAY_ALLOWED_SENDER_DOMAINS}
# Reescritura de dominio para hosts internos
MASQUERADED_DOMAINS: ${MAIL_RELAY_MASQUERADED_DOMAINS}
# Relay SMTP externo
RELAYHOST: ${MAIL_RELAY_SMARTHOST}
RELAYHOST_USERNAME: ${MAIL_RELAY_SMARTHOST_USERNAME}
RELAYHOST_PASSWORD_FILE: /run/secrets/relayhost_password
POSTFIX_smtp_tls_security_level: ${MAIL_RELAY_SMTP_TLS_SECURITY_LEVEL}
# DKIM
DKIM_AUTOGENERATE: ${MAIL_RELAY_DKIM_AUTOGENERATE}
DKIM_SELECTOR: ${MAIL_RELAY_DKIM_SELECTOR}
volumes:
- ${MAIL_RELAY_QUEUE_PATH}:/var/spool/postfix:Z
- ${MAIL_RELAY_DKIM_KEYS_PATH}:/etc/opendkim/keys:Z
- ${MAIL_RELAY_PASSWORD_FILE_PATH}:/run/secrets/relayhost_password:ro,Z
networks:
mail_internal:
ipv4_address: ${MAIL_RELAY_IPV4}
# No publicar puertos al exterior para uso interno entre contenedores.
# Descomenta para pruebas desde el host:
# ports:
# - "127.0.0.1:1587:587"
networks:
mail_internal:
name: ${MAIL_RELAY_NETWORK_NAME}
driver: bridge
ipam:
config:
- subnet: ${MAIL_RELAY_SUBNET}

29
mail-relay/stack.env Normal file
View File

@@ -0,0 +1,29 @@
##### General #####
MAIL_RELAY_IMAGE=boky/postfix:v5.1.0
TZ=Europe/Madrid
MAIL_RELAY_LOG_FORMAT=plain
##### Network #####
MAIL_RELAY_NETWORK_NAME=mail_internal
MAIL_RELAY_SUBNET=10.77.0.0/24
MAIL_RELAY_IPV4=10.77.0.10
MAIL_RELAY_MYNETWORKS=127.0.0.0/8,10.77.0.0/24
##### Relay identity #####
MAIL_RELAY_HOSTNAME=mail.example.com
MAIL_RELAY_ALLOWED_SENDER_DOMAINS=example.com
MAIL_RELAY_MASQUERADED_DOMAINS=example.com
##### Upstream SMTP (smarthost) #####
MAIL_RELAY_SMARTHOST=[in-v3.mailjet.com]:587
MAIL_RELAY_SMARTHOST_USERNAME=tu-api-key-mailjet
MAIL_RELAY_SMTP_TLS_SECURITY_LEVEL=encrypt
##### DKIM #####
MAIL_RELAY_DKIM_AUTOGENERATE=true
MAIL_RELAY_DKIM_SELECTOR=mail
##### Host paths #####
MAIL_RELAY_QUEUE_PATH=/opt/mail-relay/queue
MAIL_RELAY_DKIM_KEYS_PATH=/opt/mail-relay/opendkim
MAIL_RELAY_PASSWORD_FILE_PATH=/opt/mail-relay/secrets/relayhost_password

View File

@@ -22,9 +22,20 @@ services:
NODE_ENV: ${N8N_NODE_ENV} NODE_ENV: ${N8N_NODE_ENV}
N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED} N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED}
# Correo saliente
N8N_EMAIL_MODE: ${N8N_EMAIL_MODE}
N8N_SMTP_HOST: ${N8N_SMTP_HOST}
N8N_SMTP_PORT: ${N8N_SMTP_PORT}
N8N_SMTP_USER: ${N8N_SMTP_USER}
N8N_SMTP_PASS: ${N8N_SMTP_PASS}
N8N_SMTP_SENDER: ${N8N_SMTP_SENDER}
N8N_SMTP_SSL: ${N8N_SMTP_SSL}
N8N_SMTP_STARTTLS: ${N8N_SMTP_STARTTLS}
networks: networks:
- proxy - proxy
- n8n - n8n
- mail_internal
labels: labels:
traefik.enable: "true" traefik.enable: "true"
@@ -37,7 +48,7 @@ services:
traefik.http.routers.n8n-ui.tls.certresolver: "${TRAEFIK_CERTRESOLVER}" traefik.http.routers.n8n-ui.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
traefik.http.routers.n8n-ui.service: "n8n" traefik.http.routers.n8n-ui.service: "n8n"
traefik.http.routers.n8n-ui.priority: "10" traefik.http.routers.n8n-ui.priority: "10"
traefik.http.routers.n8n-ui.middlewares: "${TRAEFIK_AUTH_MIDDLEWARE}" # traefik.http.routers.n8n-ui.middlewares: "${TRAEFIK_AUTH_MIDDLEWARE}"
# Webhooks (NO protegidos, para que terceros puedan llamar) # Webhooks (NO protegidos, para que terceros puedan llamar)
traefik.http.routers.n8n-webhook.rule: "Host(`${N8N_DOMAIN}`) && (PathPrefix(`/webhook`) || PathPrefix(`/webhook-test`))" traefik.http.routers.n8n-webhook.rule: "Host(`${N8N_DOMAIN}`) && (PathPrefix(`/webhook`) || PathPrefix(`/webhook-test`))"
@@ -66,7 +77,9 @@ services:
networks: networks:
proxy: proxy:
external: true external: true
authentik_internal: # authentik_internal:
driver: bridge # driver: bridge
n8n: n8n:
driver: bridge driver: bridge
mail_internal:
external: true

View File

@@ -20,6 +20,16 @@ N8N_ENCRYPTION_KEY=
N8N_NODE_ENV= N8N_NODE_ENV=
N8N_DIAGNOSTICS_ENABLED= N8N_DIAGNOSTICS_ENABLED=
##### n8n - Correo (via mail-relay interno) #####
N8N_EMAIL_MODE=smtp
N8N_SMTP_HOST=mail-relay
N8N_SMTP_PORT=587
N8N_SMTP_USER=
N8N_SMTP_PASS=
N8N_SMTP_SENDER=n8n@thehomelesssherlock.com
N8N_SMTP_SSL=false
N8N_SMTP_STARTTLS=false
##### PostgreSQL interno ##### ##### PostgreSQL interno #####
POSTGRES_USER= POSTGRES_USER=
POSTGRES_PASSWORD= POSTGRES_PASSWORD=
@@ -33,4 +43,3 @@ N8N_DOMAIN=
TRAEFIK_ENTRYPOINT_SECURE= TRAEFIK_ENTRYPOINT_SECURE=
TRAEFIK_CERTRESOLVER= TRAEFIK_CERTRESOLVER=
TRAEFIK_AUTH_MIDDLEWARE= TRAEFIK_AUTH_MIDDLEWARE=

View File

@@ -0,0 +1,188 @@
services:
nextcloud-db:
image: mariadb:lts
container_name: nextcloud-db
restart: unless-stopped
command: >
--transaction-isolation=READ-COMMITTED
--binlog-format=ROW
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
environment:
TZ: ${TZ}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- /opt/nextcloud/db:/var/lib/mysql:Z
networks:
- nextcloud_internal
nextcloud-redis:
image: redis:7-alpine
container_name: nextcloud-redis
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
environment:
TZ: ${TZ}
volumes:
- /opt/nextcloud/redis:/data:Z
networks:
- nextcloud_internal
nextcloud:
image: nextcloud:33-apache
container_name: nextcloud
restart: unless-stopped
depends_on:
- nextcloud-db
- nextcloud-redis
environment:
TZ: ${TZ}
MYSQL_HOST: nextcloud-db
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
REDIS_HOST: nextcloud-redis
NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
NEXTCLOUD_TRUSTED_DOMAINS: ${NC_DOMAIN} nextcloud localhost
TRUSTED_PROXIES: ${TRUSTED_PROXIES}
OVERWRITEHOST: ${NC_DOMAIN}
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://${NC_DOMAIN}
PHP_MEMORY_LIMIT: 2048M
PHP_UPLOAD_LIMIT: 16G
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT}
SMTP_SECURE: ${SMTP_SECURE}
SMTP_AUTHTYPE: ${SMTP_AUTHTYPE}
SMTP_NAME: ${SMTP_NAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
MAIL_FROM_ADDRESS: ${MAIL_FROM_ADDRESS}
MAIL_DOMAIN: ${MAIL_DOMAIN}
volumes:
- /opt/nextcloud/html:/var/www/html:Z
- /opt/nextcloud/config:/var/www/html/config:Z
- /opt/nextcloud/data:/var/www/html/data:Z
- /opt/nextcloud/custom_apps:/var/www/html/custom_apps:Z
- /opt/nextcloud/themes:/var/www/html/themes:Z
# Opcional: exponer archivo final de Paperless en Nextcloud como solo lectura
- /opt/paperless/media:/mnt/paperless-media:ro,Z
networks:
- nextcloud_internal
- proxy
- mail_internal
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.nextcloud.rule=Host(`${NC_DOMAIN}`)
- traefik.http.routers.nextcloud.entrypoints=websecure
- traefik.http.routers.nextcloud.tls=true
- traefik.http.routers.nextcloud.tls.certresolver=${TRAEFIK_CERTRESOLVER}
- traefik.http.routers.nextcloud.middlewares=nc-dav,nc-secure-headers
- traefik.http.middlewares.nc-dav.redirectregex.permanent=true
- traefik.http.middlewares.nc-dav.redirectregex.regex=https://(.*)/.well-known/(?:card|cal)dav
- traefik.http.middlewares.nc-dav.redirectregex.replacement=https://$${1}/remote.php/dav
- traefik.http.middlewares.nc-secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.nc-secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.nc-secure-headers.headers.stsPreload=true
- traefik.http.middlewares.nc-secure-headers.headers.contentTypeNosniff=true
- traefik.http.middlewares.nc-secure-headers.headers.browserXssFilter=true
- traefik.http.services.nextcloud.loadbalancer.server.port=80
nextcloud-cron:
image: nextcloud:33-apache
container_name: nextcloud-cron
restart: unless-stopped
depends_on:
- nextcloud
entrypoint: /cron.sh
environment:
TZ: ${TZ}
MYSQL_HOST: nextcloud-db
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
REDIS_HOST: nextcloud-redis
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT}
SMTP_SECURE: ${SMTP_SECURE}
SMTP_AUTHTYPE: ${SMTP_AUTHTYPE}
SMTP_NAME: ${SMTP_NAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
MAIL_FROM_ADDRESS: ${MAIL_FROM_ADDRESS}
MAIL_DOMAIN: ${MAIL_DOMAIN}
volumes:
- /opt/nextcloud/html:/var/www/html:Z
- /opt/nextcloud/config:/var/www/html/config:Z
- /opt/nextcloud/data:/var/www/html/data:Z
- /opt/nextcloud/custom_apps:/var/www/html/custom_apps:Z
- /opt/nextcloud/themes:/var/www/html/themes:Z
# Opcional: exponer archivo final de Paperless en Nextcloud como solo lectura
- /opt/paperless/media:/mnt/paperless-media:ro,Z
networks:
- nextcloud_internal
- mail_internal
onlyoffice-documentserver:
image: onlyoffice/documentserver:9.3.1
container_name: onlyoffice-documentserver
restart: unless-stopped
environment:
TZ: ${TZ}
JWT_ENABLED: "true"
JWT_SECRET: ${OO_JWT_SECRET}
JWT_HEADER: Authorization
SECURE_LINK_SECRET: ${OO_SECURE_LINK_SECRET}
ALLOW_PRIVATE_IP_ADDRESS: "true"
volumes:
- /opt/onlyoffice/logs:/var/log/onlyoffice:Z
- /opt/onlyoffice/data:/var/www/onlyoffice/Data:Z
- /opt/onlyoffice/lib:/var/lib/onlyoffice:Z
- /opt/onlyoffice/postgresql:/var/lib/postgresql:Z
#- /opt/onlyoffice/plugins:/var/www/onlyoffice/documentserver/sdkjs-plugins:Z
networks:
- nextcloud_internal
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.onlyoffice.rule=Host(`${OO_DOMAIN}`)
- traefik.http.routers.onlyoffice.entrypoints=websecure
- traefik.http.routers.onlyoffice.tls=true
- traefik.http.routers.onlyoffice.tls.certresolver=${TRAEFIK_CERTRESOLVER}
- traefik.http.routers.onlyoffice.middlewares=oo-secure-headers,oo-forwarded
- traefik.http.middlewares.oo-secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.oo-secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.oo-secure-headers.headers.stsPreload=true
- traefik.http.middlewares.oo-secure-headers.headers.contentTypeNosniff=true
- traefik.http.middlewares.oo-forwarded.headers.customRequestHeaders.X-Forwarded-Proto=https
- traefik.http.middlewares.oo-forwarded.headers.customRequestHeaders.X-Forwarded-Host=${OO_DOMAIN}
- traefik.http.middlewares.oo-forwarded.headers.customRequestHeaders.X-Forwarded-Port=443
- traefik.http.middlewares.oo-forwarded.headers.customRequestHeaders.X-Forwarded-Ssl=on
- traefik.http.services.onlyoffice.loadbalancer.server.port=80
networks:
nextcloud_internal:
driver: bridge
proxy:
external: true
mail_internal:
external: true

21
nextcloud/stack.env Normal file
View File

@@ -0,0 +1,21 @@
TZ=Europe/Madrid
NC_DOMAIN=nextcloud.example.com
OO_DOMAIN=onlyoffice.example.com
TRAEFIK_CERTRESOLVER=letsencrypt
TRUSTED_PROXIES=10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
MYSQL_ROOT_PASSWORD=change_me_mysql_root_password_long_and_secure
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=change_me_nextcloud_db_password_long_and_secure
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=change_me_nextcloud_admin_password_long_and_secure
OO_JWT_SECRET=change_me_onlyoffice_jwt_secret_long_and_random
OO_SECURE_LINK_SECRET=change_me_onlyoffice_secure_link_secret_long_and_random
SMTP_HOST=mail-relay
SMTP_PORT=587
SMTP_SECURE=tls
SMTP_AUTHTYPE=
SMTP_NAME=
SMTP_PASSWORD=
MAIL_FROM_ADDRESS=nextcloud
MAIL_DOMAIN=example.com

View File

@@ -0,0 +1,171 @@
services:
paperless-db:
image: postgres:18
container_name: paperless-db
restart: unless-stopped
environment:
TZ: ${TZ}
POSTGRES_DB: ${PAPERLESS_DBNAME}
POSTGRES_USER: ${PAPERLESS_DBUSER}
POSTGRES_PASSWORD: ${PAPERLESS_DBPASS}
volumes:
- /opt/paperless/pgdata:/var/lib/postgresql:Z
networks:
- paperless_internal
paperless-redis:
image: redis:8
container_name: paperless-redis
restart: unless-stopped
volumes:
- /opt/paperless/redis:/data:Z
networks:
- paperless_internal
paperless-gotenberg:
image: gotenberg/gotenberg:8.27
container_name: paperless-gotenberg
restart: unless-stopped
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
networks:
- paperless_internal
paperless-tika:
image: apache/tika:latest
container_name: paperless-tika
restart: unless-stopped
networks:
- paperless_internal
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
container_name: paperless
restart: unless-stopped
depends_on:
- paperless-db
- paperless-redis
- paperless-gotenberg
- paperless-tika
environment:
TZ: ${TZ}
PAPERLESS_REDIS: redis://paperless-redis:6379
PAPERLESS_DBHOST: paperless-db
PAPERLESS_DBENGINE: postgresql
PAPERLESS_DBNAME: ${PAPERLESS_DBNAME}
PAPERLESS_DBUSER: ${PAPERLESS_DBUSER}
PAPERLESS_DBPASS: ${PAPERLESS_DBPASS}
PAPERLESS_URL: https://${PAPERLESS_DOMAIN}
PAPERLESS_SECRET_KEY: ${PAPERLESS_SECRET_KEY}
PAPERLESS_ALLOWED_HOSTS: ${PAPERLESS_ALLOWED_HOSTS}
PAPERLESS_CSRF_TRUSTED_ORIGINS: https://${PAPERLESS_DOMAIN}
PAPERLESS_TRUSTED_PROXIES: ${TRUSTED_PROXIES}
PAPERLESS_ADMIN_USER: ${PAPERLESS_ADMIN_USER}
PAPERLESS_ADMIN_PASSWORD: ${PAPERLESS_ADMIN_PASSWORD}
PAPERLESS_ADMIN_MAIL: ${PAPERLESS_ADMIN_MAIL}
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_ENDPOINT: http://paperless-tika:9998
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://paperless-gotenberg:3000
# Más robusto cuando los ficheros llegan por sync/mount y no por inotify puro
PAPERLESS_CONSUMER_POLLING: ${PAPERLESS_CONSUMER_POLLING}
volumes:
- /opt/paperless/data:/usr/src/paperless/data:Z
- /opt/paperless/media:/usr/src/paperless/media:Z
- /opt/paperless/export:/usr/src/paperless/export:Z
- /opt/paperless/consume:/usr/src/paperless/consume:Z
networks:
- paperless_internal
- proxy
- mail_internal
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.paperless.rule=Host(`${PAPERLESS_DOMAIN}`)
- traefik.http.routers.paperless.entrypoints=websecure
- traefik.http.routers.paperless.tls=true
- traefik.http.routers.paperless.tls.certresolver=${TRAEFIK_CERTRESOLVER}
- traefik.http.routers.paperless.middlewares=paperless-secure-headers
- traefik.http.middlewares.paperless-secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.paperless-secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.paperless-secure-headers.headers.stsPreload=true
- traefik.http.middlewares.paperless-secure-headers.headers.contentTypeNosniff=true
- traefik.http.middlewares.paperless-secure-headers.headers.browserXssFilter=true
- traefik.http.services.paperless.loadbalancer.server.port=8000
paperless-ai:
image: clusterzx/paperless-ai:latest
container_name: paperless-ai
restart: unless-stopped
depends_on:
- paperless
environment:
TZ: ${TZ}
volumes:
- /opt/paperless-ai/data:/app/data:Z
networks:
- paperless_internal
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.paperless-ai.rule=Host(`${PAPERLESS_AI_DOMAIN}`)
- traefik.http.routers.paperless-ai.entrypoints=websecure
- traefik.http.routers.paperless-ai.tls=true
- traefik.http.routers.paperless-ai.tls.certresolver=${TRAEFIK_CERTRESOLVER}
- traefik.http.routers.paperless-ai.middlewares=paperless-ai-secure-headers
- traefik.http.middlewares.paperless-ai-secure-headers.headers.stsSeconds=31536000
- traefik.http.middlewares.paperless-ai-secure-headers.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.paperless-ai-secure-headers.headers.stsPreload=true
- traefik.http.middlewares.paperless-ai-secure-headers.headers.contentTypeNosniff=true
- traefik.http.services.paperless-ai.loadbalancer.server.port=3000
# Sync unidireccional: Nextcloud/Paperless-Inbox -> paperless/consume
paperless-inbox-sync:
image: rclone/rclone:latest
container_name: paperless-inbox-sync
restart: unless-stopped
depends_on:
- paperless
entrypoint:
- /bin/sh
- /rclone-sync.sh
environment:
TZ: ${TZ}
RCLONE_CONFIG_NC_TYPE: webdav
RCLONE_CONFIG_NC_URL: https://${NC_DOMAIN}/remote.php/dav/files/${NC_WEBDAV_USER}
RCLONE_CONFIG_NC_VENDOR: nextcloud
RCLONE_CONFIG_NC_USER: ${NC_WEBDAV_USER}
RCLONE_CONFIG_NC_PASS: ${NC_WEBDAV_PASS}
RCLONE_SYNC_INTERVAL: ${RCLONE_SYNC_INTERVAL}
PAPERLESS_INBOX_DIR: ${PAPERLESS_INBOX_DIR}
volumes:
- /opt/paperless/consume:/consume:Z
- /opt/rclone:/config/rclone:Z
- /opt/paperless/rclone-sync.sh:/rclone-sync.sh:ro,Z
networks:
- paperless_internal
networks:
paperless_internal:
driver: bridge
proxy:
external: true
mail_internal:
external: true

19
paperless/stack.env Normal file
View File

@@ -0,0 +1,19 @@
TZ=Europe/Madrid
NC_DOMAIN=nextcloud.example.com
PAPERLESS_DOMAIN=paperless.example.com
PAPERLESS_AI_DOMAIN=paperless-ai.example.com
TRAEFIK_CERTRESOLVER=letsencrypt
TRUSTED_PROXIES=10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
PAPERLESS_DBNAME=paperless
PAPERLESS_DBUSER=paperless
PAPERLESS_DBPASS=change_me_paperless_db_password_long_and_secure
PAPERLESS_SECRET_KEY=change_me_paperless_secret_key_long_and_random_string
PAPERLESS_ADMIN_USER=admin
PAPERLESS_ADMIN_PASSWORD=change_me_paperless_admin_password_long_and_secure
PAPERLESS_ADMIN_MAIL=admin@example.com
PAPERLESS_CONSUMER_POLLING=60
PAPERLESS_ALLOWED_HOSTS=paperless.example.com,paperless,localhost
NC_WEBDAV_USER=paperless
NC_WEBDAV_PASS=change_me_nextcloud_webdav_password_long_and_random
PAPERLESS_INBOX_DIR=Paperless-Inbox
RCLONE_SYNC_INTERVAL=60

View File

@@ -21,12 +21,19 @@ services:
traefik.enable: "true" traefik.enable: "true"
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}" traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
# Router HTTPS (dos dominios válidos, usando OR) # Router HTTPS - dominio principal
traefik.http.routers.trilium.rule: "Host(`${TRILIUM_DOMAIN_1}`) || Host(`${TRILIUM_DOMAIN_2}`)" traefik.http.routers.trilium.rule: "Host(`${TRILIUM_DOMAIN_1}`)"
traefik.http.routers.trilium.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}" traefik.http.routers.trilium.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
traefik.http.routers.trilium.tls: "true" traefik.http.routers.trilium.tls: "true"
traefik.http.routers.trilium.tls.certresolver: "${TRAEFIK_CERTRESOLVER}" traefik.http.routers.trilium.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
# Router HTTPS - dominio secundario (sin redirección)
traefik.http.routers.trilium-alt.rule: "Host(`${TRILIUM_DOMAIN_2}`)"
traefik.http.routers.trilium-alt.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
traefik.http.routers.trilium-alt.tls: "true"
traefik.http.routers.trilium-alt.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
traefik.http.routers.trilium-alt.service: "trilium@docker"
# Servicio interno # Servicio interno
traefik.http.services.trilium.loadbalancer.server.port: "${TRILIUM_HTTP_PORT}" traefik.http.services.trilium.loadbalancer.server.port: "${TRILIUM_HTTP_PORT}"