Compare commits
8 Commits
list
...
81c7b45069
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81c7b45069 | ||
|
|
d484dd5e5f | ||
|
|
8bd6b3b9f2 | ||
|
|
24270af2f0 | ||
|
|
1f7ed5071d | ||
|
|
59cc0c0bab | ||
|
|
411ebf968d | ||
|
|
8ff1456a3b |
137
mail-relay/README.md
Normal file
137
mail-relay/README.md
Normal 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
|
||||
52
mail-relay/docker-compose.yml
Normal file
52
mail-relay/docker-compose.yml
Normal 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
29
mail-relay/stack.env
Normal 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
|
||||
@@ -22,9 +22,20 @@ services:
|
||||
NODE_ENV: ${N8N_NODE_ENV}
|
||||
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:
|
||||
- proxy
|
||||
- n8n
|
||||
- mail_internal
|
||||
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
@@ -37,7 +48,7 @@ services:
|
||||
traefik.http.routers.n8n-ui.tls.certresolver: "${TRAEFIK_CERTRESOLVER}"
|
||||
traefik.http.routers.n8n-ui.service: "n8n"
|
||||
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)
|
||||
traefik.http.routers.n8n-webhook.rule: "Host(`${N8N_DOMAIN}`) && (PathPrefix(`/webhook`) || PathPrefix(`/webhook-test`))"
|
||||
@@ -66,7 +77,9 @@ services:
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
authentik_internal:
|
||||
driver: bridge
|
||||
# authentik_internal:
|
||||
# driver: bridge
|
||||
n8n:
|
||||
driver: bridge
|
||||
mail_internal:
|
||||
external: true
|
||||
|
||||
@@ -20,6 +20,16 @@ N8N_ENCRYPTION_KEY=
|
||||
N8N_NODE_ENV=
|
||||
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 #####
|
||||
POSTGRES_USER=
|
||||
POSTGRES_PASSWORD=
|
||||
@@ -33,4 +43,3 @@ N8N_DOMAIN=
|
||||
TRAEFIK_ENTRYPOINT_SECURE=
|
||||
TRAEFIK_CERTRESOLVER=
|
||||
TRAEFIK_AUTH_MIDDLEWARE=
|
||||
|
||||
|
||||
188
nextcloud/docker-compose.yml
Normal file
188
nextcloud/docker-compose.yml
Normal 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
21
nextcloud/stack.env
Normal 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
|
||||
171
paperless/docker-compose.yml
Normal file
171
paperless/docker-compose.yml
Normal 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
19
paperless/stack.env
Normal 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
|
||||
@@ -21,12 +21,19 @@ services:
|
||||
traefik.enable: "true"
|
||||
traefik.docker.network: "${TRAEFIK_DOCKER_NETWORK}"
|
||||
|
||||
# Router HTTPS (dos dominios válidos, usando OR)
|
||||
traefik.http.routers.trilium.rule: "Host(`${TRILIUM_DOMAIN_1}`) || Host(`${TRILIUM_DOMAIN_2}`)"
|
||||
# Router HTTPS - dominio principal
|
||||
traefik.http.routers.trilium.rule: "Host(`${TRILIUM_DOMAIN_1}`)"
|
||||
traefik.http.routers.trilium.entrypoints: "${TRAEFIK_ENTRYPOINT_SECURE}"
|
||||
traefik.http.routers.trilium.tls: "true"
|
||||
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
|
||||
traefik.http.services.trilium.loadbalancer.server.port: "${TRILIUM_HTTP_PORT}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user