From 5c6b312062098ef41559341ae9a1c9544b79f013 Mon Sep 17 00:00:00 2001 From: melbv Date: Mon, 6 Jul 2026 16:49:41 +0200 Subject: [PATCH 1/7] feat: add mail-archiver service --- services/mail-archiver/.env | 36 +++++++++ services/mail-archiver/README.md | 13 ++++ services/mail-archiver/compose.yaml | 114 ++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 services/mail-archiver/.env create mode 100644 services/mail-archiver/README.md create mode 100644 services/mail-archiver/compose.yaml diff --git a/services/mail-archiver/.env b/services/mail-archiver/.env new file mode 100644 index 00000000..6fe09583 --- /dev/null +++ b/services/mail-archiver/.env @@ -0,0 +1,36 @@ +#version=1.1 +#URL=https://github.com/tailscale-dev/ScaleTail +#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. + +# Service Configuration +SERVICE=MailArchiver # Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). +IMAGE_URL=s1t5/mailarchiver:latest # Docker image URL from container registry (e.g., adguard/adguard-home). + +# Network Configuration +SERVICEPORT=5000 # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. + +# Tailscale Configuration +TS_AUTHKEY= # Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. + +# Optional Service variables +# PUID=1000 + +#Time Zone setting for containers +TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/ + +# Defines the first admin user +APP_USER= +APP_PW= + +# Defines the DB user +DB_USER= +DB_PW= + +#OAuth connectivity +OAUTH_SETUP=false # overwrite to 'true' if you want to use OAuth +OAUTH_URL= +OAUTH_CLIENTID= +OAUTH_SECRET= diff --git a/services/mail-archiver/README.md b/services/mail-archiver/README.md new file mode 100644 index 00000000..1a140b43 --- /dev/null +++ b/services/mail-archiver/README.md @@ -0,0 +1,13 @@ +# Mail Archiver with Tailscale Sidecar Configuration + +This Docker Compose configuration sets up [Mail Archiver](https://mail-archiver.org/) with Tailscale as a sidecar container to keep the app reachable over your Tailnet. + +## Mail Archiver + +[Mail Archiver](https://mail-archiver.org/) is an open-source and self hosted email archiver with powerful features, including a wide array of email sources (IMAP, M365, import files), a powerful search feature scanning email metadata and content and the ability to sync all your mailboxes to your account. +This configuration leverages Tailscale to securely connect to your Mail Archiver instance, ensuring that your email archiving interface is protected from unauthorized access and that your instance is accessible only via your private Tailscale network. + + +## Configuration Overview + +In this setup, the `tailscale-mailarchive-app` service runs Tailscale, which manages secure networking for Mail Archiver. The `mailarchive-app` service utilizes the Tailscale network stack via Docker's `network_mode: service:` configuration. This keeps the app Tailnet-only unless you intentionally expose ports. diff --git a/services/mail-archiver/compose.yaml b/services/mail-archiver/compose.yaml new file mode 100644 index 00000000..865853fd --- /dev/null +++ b/services/mail-archiver/compose.yaml @@ -0,0 +1,114 @@ +configs: + ts-serve: + content: | + {"TCP":{"443":{"HTTPS":true}}, + "Web":{"$${TS_CERT_DOMAIN}:443": + {"Handlers":{"/": + {"Proxy":"http://127.0.0.1:5000"}}}}, + "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} + +services: + # Make sure you have updated/checked the .env file with the correct variables. + # All the ${ xx } need to be defined there. + # Tailscale Sidecar Configuration + tailscale: + image: tailscale/tailscale:latest # Image to be used + container_name: tailscale-${SERVICE} # Name for local container management + hostname: tailscale-${SERVICE} # Name used within your Tailscale environment + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_EXTRA_ARGS=--advertise-tags=tag:container + - TS_STATE_DIR=/var/lib/tailscale + - TS_SERVE_CONFIG=/config/serve-config.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required + - TS_USERSPACE=false + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_AUTH_ONCE=true + configs: + - source: ts-serve + target: /config/serve.json + volumes: + - ./config:/config # Config folder used to store Tailscale files - you may need to change the path + - ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path + devices: + - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work + cap_add: + - net_admin # Tailscale requirement + networks: + - postgres + #ports: + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICE}PORT to the local network - may be removed if only exposure to your Tailnet is required + # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below + #dns: + # - ${DNS_SERVER} + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + restart: always + + application: + image: ${IMAGE_URL} + container_name: ${SERVICE}-app + restart: always + network_mode: service:tailscale + environment: + # Database Connection + - ConnectionStrings__DefaultConnection=Host=postgres;Database=${SERVICE};Username=${DB_USER};Password=${DB_PW}; + + # Authentication Settings + - Authentication__Username=${APP_USER} + - Authentication__Password=${APP_PW} + + # TimeZone Settings + - TZ=Europe/Amsterdam + + # OIDC Configuration + - OAuth__Enabled=${OAUTH_SETUP} + - OAuth__Authority=${OAUTH_URL} + - OAuth__ClientId=${OAUTH_CLIENTID} + - OAuth__ClientSecret=${OAUTH_SECRET} + - OAuth__DisplayName=PocketID SSO + - OAuth__ClientScopes__0=openid + - OAuth__ClientScopes__1=profile + - OAuth__ClientScopes__2=email + - OAuth__DisablePasswordLogin=false + - OAuth__AutoApproveUsers=false + + volumes: + - ./data-protection-keys:/app/DataProtection-Keys + depends_on: + tailscale: + condition: service_healthy + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 15s + + postgres: + image: postgres:17-alpine + restart: always + environment: + POSTGRES_DB: ${SEERVICE} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PW} + volumes: + - ./postgres-data:/var/lib/postgresql/data + networks: + - postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${SERVICE}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + +networks: + postgres: From d59817020236be55ab112b3fc65b5176d3ea950c Mon Sep 17 00:00:00 2001 From: melbv Date: Mon, 6 Jul 2026 17:07:15 +0200 Subject: [PATCH 2/7] feat: add mail-archiver service --- services/mail-archiver/compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/mail-archiver/compose.yaml b/services/mail-archiver/compose.yaml index 865853fd..056a5451 100644 --- a/services/mail-archiver/compose.yaml +++ b/services/mail-archiver/compose.yaml @@ -96,7 +96,7 @@ services: image: postgres:17-alpine restart: always environment: - POSTGRES_DB: ${SEERVICE} + POSTGRES_DB: ${SERVICE} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PW} volumes: From bce792a9aaf0220478323e38b4da9e04c74a041e Mon Sep 17 00:00:00 2001 From: Jack Spiering <46534141+jackspiering@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:48:13 +0200 Subject: [PATCH 3/7] Mail Archiver: align with the service template and fix startup - Remove the tag:container advertise flag; the sidecar failed to log in with "requested tags are invalid or not permitted" on tailnets without that tag. - Run PostgreSQL in the sidecar's network namespace and connect over 127.0.0.1, instead of a separate network the app could not reach. - Use template naming (hostname, app-/db container names, serve.json, ${SERVICE}-data bind mounts) and template healthcheck settings. - Replace the curl healthcheck, which always failed because the image ships no HTTP client, with a pgrep check on MailArchiver.dll. - Pass TZ from .env and use it for the web interface time zone. - Make the OAuth button label configurable instead of "PocketID SSO". - Move .env comments onto their own lines and document required passwords. - Rewrite the README per the template, with first-start notes, the OIDC redirect URI, and volume descriptions. --- services/mail-archiver/.env | 29 +++++---- services/mail-archiver/README.md | 42 +++++++++++-- services/mail-archiver/compose.yaml | 93 +++++++++++++---------------- 3 files changed, 99 insertions(+), 65 deletions(-) diff --git a/services/mail-archiver/.env b/services/mail-archiver/.env index 6fe09583..86142c7c 100644 --- a/services/mail-archiver/.env +++ b/services/mail-archiver/.env @@ -3,34 +3,41 @@ #COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. # Service Configuration -SERVICE=MailArchiver # Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). -IMAGE_URL=s1t5/mailarchiver:latest # Docker image URL from container registry (e.g., adguard/adguard-home). +# Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). +SERVICE=mail-archiver +# Docker image URL from container registry (e.g., adguard/adguard-home). +IMAGE_URL=s1t5/mailarchiver:latest # Network Configuration -SERVICEPORT=5000 # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +# Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +SERVICEPORT=5000 DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. # Tailscale Configuration -TS_AUTHKEY= # Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. +# Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. +TS_AUTHKEY= # Optional Service variables # PUID=1000 -#Time Zone setting for containers +# Time Zone setting for containers TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones # Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/ -# Defines the first admin user -APP_USER= +# First admin user. Required: set a password before the first start. +APP_USER=admin APP_PW= -# Defines the DB user -DB_USER= +# Database user. Required: set a password before the first start. Use letters and digits only, because it is part of the connection string. +# PostgreSQL applies it only when the database is first created. Existing installations must keep their current password. +DB_USER=mailuser DB_PW= -#OAuth connectivity -OAUTH_SETUP=false # overwrite to 'true' if you want to use OAuth +# OAuth/OIDC login (optional). Set OAUTH_SETUP to true and fill in the values from your identity provider. +OAUTH_SETUP=false OAUTH_URL= OAUTH_CLIENTID= OAUTH_SECRET= +# Name shown on the login button ("Login with ..."). +OAUTH_DISPLAYNAME=SSO diff --git a/services/mail-archiver/README.md b/services/mail-archiver/README.md index 1a140b43..a029ea1f 100644 --- a/services/mail-archiver/README.md +++ b/services/mail-archiver/README.md @@ -4,10 +4,44 @@ This Docker Compose configuration sets up [Mail Archiver](https://mail-archiver. ## Mail Archiver -[Mail Archiver](https://mail-archiver.org/) is an open-source and self hosted email archiver with powerful features, including a wide array of email sources (IMAP, M365, import files), a powerful search feature scanning email metadata and content and the ability to sync all your mailboxes to your account. -This configuration leverages Tailscale to securely connect to your Mail Archiver instance, ensuring that your email archiving interface is protected from unauthorized access and that your instance is accessible only via your private Tailscale network. - +[Mail Archiver](https://mail-archiver.org/) is an open-source, self-hosted email archiver. It collects mail from IMAP accounts, Microsoft 365, and imported files, and offers full-text search across message metadata and content. Pairing it with Tailscale keeps your email archive off the public internet and reachable only from devices on your Tailnet. ## Configuration Overview -In this setup, the `tailscale-mailarchive-app` service runs Tailscale, which manages secure networking for Mail Archiver. The `mailarchive-app` service utilizes the Tailscale network stack via Docker's `network_mode: service:` configuration. This keeps the app Tailnet-only unless you intentionally expose ports. +In this setup, the `tailscale` service (container `tailscale-mail-archiver`) runs Tailscale, which manages secure networking for Mail Archiver. The `application` service (container `app-mail-archiver`) uses the Tailscale network stack via Docker's `network_mode: service:tailscale` configuration. This keeps the app Tailnet-only unless you intentionally expose ports. + +The `db` service (container `app-mail-archiver-db`) runs PostgreSQL in the same network namespace, so Mail Archiver reaches it at `127.0.0.1`. The database is not exposed outside the stack. + +## Before the first start + +- Set `TS_AUTHKEY`, `APP_PW`, and `DB_PW` in `.env`. The stack does not work with empty passwords. +- Use only letters and digits in `DB_PW`. The password is inserted into the database connection string, so characters such as `;` or `=` break it. +- PostgreSQL applies `DB_USER` and `DB_PW` only when it creates the database on the first start. Changing them later in `.env` does not change the existing database user. +- `APP_USER` and `APP_PW` define the first admin account. Log in with them after the first start. + +## Access + +Tailscale Serve publishes the web interface over HTTPS at `https://mail-archiver..ts.net`. Serve proxies to Mail Archiver on port `5000`; this port is set in the `ts-serve` config in `compose.yaml` and does not follow `SERVICEPORT`. The first request can take a few seconds while Tailscale issues the certificate. + +The commented `ports:` mapping is not needed for Tailnet access. Enable it only if you also want to reach Mail Archiver on port `5000` from your local network. + +## OAuth/OIDC login (optional) + +To log in through an identity provider, set `OAUTH_SETUP=true` and fill in `OAUTH_URL`, `OAUTH_CLIENTID`, and `OAUTH_SECRET`. Use `https://mail-archiver..ts.net/oidc-signin-completed` as the redirect URI in your provider. `OAUTH_DISPLAYNAME` sets the text on the login button. Password login stays enabled, and new OAuth users need approval by an admin. + +## Volumes + +- `./mail-archiver-data/db` stores the PostgreSQL database, which holds the archived mail. +- `./mail-archiver-data/data-protection-keys` stores the keys that protect login cookies. Keeping it means users stay logged in when the container is recreated. + +## Files to check + +Please check the following contents for validity as some variables need to be defined upfront. + +- `.env` // Main variables `TS_AUTHKEY`, `APP_PW`, `DB_PW` + +## Useful links + +- [Mail Archiver website](https://mail-archiver.org/) +- [Mail Archiver on GitHub](https://github.com/s1t5/mail-archiver) +- [Mail Archiver documentation](https://github.com/s1t5/mail-archiver/tree/main/doc) diff --git a/services/mail-archiver/compose.yaml b/services/mail-archiver/compose.yaml index 056a5451..01abb6d1 100644 --- a/services/mail-archiver/compose.yaml +++ b/services/mail-archiver/compose.yaml @@ -8,22 +8,21 @@ configs: "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} services: - # Make sure you have updated/checked the .env file with the correct variables. - # All the ${ xx } need to be defined there. +# Make sure you have updated/checked the .env file with the correct variables. +# All the ${ xx } need to be defined there. # Tailscale Sidecar Configuration tailscale: image: tailscale/tailscale:latest # Image to be used container_name: tailscale-${SERVICE} # Name for local container management - hostname: tailscale-${SERVICE} # Name used within your Tailscale environment + hostname: ${SERVICE} # Name used within your Tailscale environment environment: - TS_AUTHKEY=${TS_AUTHKEY} - - TS_EXTRA_ARGS=--advertise-tags=tag:container - TS_STATE_DIR=/var/lib/tailscale - - TS_SERVE_CONFIG=/config/serve-config.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required + - TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required - TS_USERSPACE=false - - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" - - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint - #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment only if the service must resolve MagicDNS names - this replaces Docker DNS, so Compose service names no longer resolve - TS_AUTH_ONCE=true configs: - source: ts-serve @@ -35,8 +34,6 @@ services: - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work cap_add: - net_admin # Tailscale requirement - networks: - - postgres #ports: # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICE}PORT to the local network - may be removed if only exposure to your Tailnet is required # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below @@ -50,65 +47,61 @@ services: start_period: 10s # Time to wait before starting health checks restart: always + # ${SERVICE} application: - image: ${IMAGE_URL} - container_name: ${SERVICE}-app - restart: always - network_mode: service:tailscale - environment: - # Database Connection - - ConnectionStrings__DefaultConnection=Host=postgres;Database=${SERVICE};Username=${DB_USER};Password=${DB_PW}; - - # Authentication Settings + image: ${IMAGE_URL} # Image to be used + network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + container_name: app-${SERVICE} # Name for local container management + environment: # Variables are declared in .env file. + # Database connection + - ConnectionStrings__DefaultConnection=Host=127.0.0.1;Database=MailArchiver;Username=${DB_USER};Password=${DB_PW}; + # First admin user - Authentication__Username=${APP_USER} - Authentication__Password=${APP_PW} - - # TimeZone Settings - - TZ=Europe/Amsterdam - - # OIDC Configuration + # Time zone + - TZ=${TZ} + - TimeZone__DisplayTimeZoneId=${TZ} # Time zone used for dates in the web interface + # OAuth/OIDC login - OAuth__Enabled=${OAUTH_SETUP} - OAuth__Authority=${OAUTH_URL} - OAuth__ClientId=${OAUTH_CLIENTID} - OAuth__ClientSecret=${OAUTH_SECRET} - - OAuth__DisplayName=PocketID SSO + - OAuth__DisplayName=${OAUTH_DISPLAYNAME} - OAuth__ClientScopes__0=openid - OAuth__ClientScopes__1=profile - OAuth__ClientScopes__2=email - OAuth__DisablePasswordLogin=false - OAuth__AutoApproveUsers=false - volumes: - - ./data-protection-keys:/app/DataProtection-Keys + - ./${SERVICE}-data/data-protection-keys:/app/DataProtection-Keys depends_on: tailscale: condition: service_healthy - postgres: + db: condition: service_healthy healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000"] - interval: 5s - timeout: 3s - retries: 10 - start_period: 15s - - postgres: - image: postgres:17-alpine + test: ["CMD", "pgrep", "-f", "MailArchiver.dll"] # Check if the Mail Archiver process is running; the image has no HTTP client + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 30s # Time to wait before starting health checks restart: always + + # Database + db: + image: postgres:17-alpine # Image to be used + network_mode: service:tailscale # Join the same network namespace to be accessible via localhost + container_name: app-${SERVICE}-db # Name for local container management environment: - POSTGRES_DB: ${SERVICE} - POSTGRES_USER: ${DB_USER} - POSTGRES_PASSWORD: ${DB_PW} + - POSTGRES_DB=MailArchiver + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PW} volumes: - - ./postgres-data:/var/lib/postgresql/data - networks: - - postgres + - ./${SERVICE}-data/db:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${SERVICE}"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - -networks: - postgres: + test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U ${DB_USER} -d MailArchiver"] # Check if PostgreSQL accepts connections + interval: 10s # How often to perform the check + timeout: 5s # Time to wait for the check to succeed + retries: 5 # Number of retries before marking as unhealthy + start_period: 30s # Time to wait before starting health checks + restart: always From 22710528ad2cf7c9e74019ff1b3e5d45396c172b Mon Sep 17 00:00:00 2001 From: Jack Spiering <46534141+jackspiering@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:37:21 +0200 Subject: [PATCH 4/7] Mail Archiver: add to the service list and document prerequisites --- README.md | 1 + services/mail-archiver/README.md | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/README.md b/README.md index 558dad95..ed559b4e 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ ScaleTail provides ready-to-run [Docker Compose](https://docs.docker.com/compose | 🗂️ **Kaneo** | A modern, self-hosted project management platform focused on simplicity. | [Details](services/kaneo) | | 🧠 **LanguageTool** | An open-source proofreading software for multiple languages. | [Details](services/languagetool) | | 🔖 **Linkding** | A self-hosted bookmark manager to save and organize links. | [Details](services/linkding) | +| 📨 **Mail Archiver** | An open-source, self-hosted email archiver for IMAP and Microsoft 365 mailboxes, with full-text search across archived mail. | [Details](services/mail-archiver) | | 📥 **Mattermost** | A self-hosted collaborative workflow and communication tool. | [Details](services/mattermost) | | 📝 **Memos** | A lightweight, self-hosted note-taking and knowledge management platform for capturing ideas, daily notes, and personal knowledge. | [Details](services/memos) | | 📝 **Nanote** | A lightweight, self-hosted note-taking app with Markdown support. | [Details](services/nanote) | diff --git a/services/mail-archiver/README.md b/services/mail-archiver/README.md index a029ea1f..71bad2d5 100644 --- a/services/mail-archiver/README.md +++ b/services/mail-archiver/README.md @@ -12,6 +12,10 @@ In this setup, the `tailscale` service (container `tailscale-mail-archiver`) run The `db` service (container `app-mail-archiver-db`) runs PostgreSQL in the same network namespace, so Mail Archiver reaches it at `127.0.0.1`. The database is not exposed outside the stack. +## Prerequisites + +The stack needs no extra user groups or devices beyond `/dev/net/tun` for the Tailscale container. Both containers start as root, so Docker can create the `mail-archiver-data` folders on the first start; you do not need to create them beforehand. + ## Before the first start - Set `TS_AUTHKEY`, `APP_PW`, and `DB_PW` in `.env`. The stack does not work with empty passwords. From 9aeb7229935ae9967745ee0c48c365f674084f68 Mon Sep 17 00:00:00 2001 From: melbv Date: Tue, 13 Oct 2026 18:03:36 +0200 Subject: [PATCH 5/7] Adding mail-archiver to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5a9fef23..71c20f51 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ Every stack starts from the same [standard setup](documentation/standard-setup.m | **Karakeep** | Bookmark manager that archives links, notes, and images, with full-text search. | [Details](services/karakeep) | | **LanguageTool** | Grammar and spelling checker server for browser extensions, with no web interface. | [Details](services/languagetool) | | **linkding** | Bookmark manager to save, tag, and search links, with a browser extension. | [Details](services/linkding) | +| **Mail Archiver** | Archives incoming mail and makes it searchable from the browser. | [Details](services/mail-archiver) | | **Mattermost** | Team chat platform with channels, direct messages, and integrations. | [Details](services/mattermost) | | **Memos** | Note-taking app for short Markdown notes, shown on a timeline. | [Details](services/memos) | | **Nanote** | Note-taking app that stores notes as Markdown files in folders. | [Details](services/nanote) | From 8e5765864475f41a032c1433498eab6884839bd3 Mon Sep 17 00:00:00 2001 From: melbv Date: Tue, 13 Oct 2026 18:09:42 +0200 Subject: [PATCH 6/7] fix: require APP_PW/DB_PW, restrict postgres to localhost, align with template --- services/mail-archiver/.env | 12 ++++--- services/mail-archiver/README.md | 52 +++++++++++------------------ services/mail-archiver/compose.yaml | 13 ++++---- 3 files changed, 34 insertions(+), 43 deletions(-) diff --git a/services/mail-archiver/.env b/services/mail-archiver/.env index 86142c7c..e93799c6 100644 --- a/services/mail-archiver/.env +++ b/services/mail-archiver/.env @@ -10,20 +10,24 @@ IMAGE_URL=s1t5/mailarchiver:latest # Network Configuration # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +# Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. SERVICEPORT=5000 -DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. +DNS_SERVER=9.9.9.9 # Tailscale Configuration -# Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. +# Auth key from https://tailscale.com/admin/authkeys. +# See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. TS_AUTHKEY= # Optional Service variables # PUID=1000 # Time Zone setting for containers -TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +TZ=Europe/Amsterdam -# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/ +# Any Container environment variables are declared below. +# See https://docs.docker.com/compose/how-tos/environment-variables/ # First admin user. Required: set a password before the first start. APP_USER=admin diff --git a/services/mail-archiver/README.md b/services/mail-archiver/README.md index 71bad2d5..45960905 100644 --- a/services/mail-archiver/README.md +++ b/services/mail-archiver/README.md @@ -1,51 +1,37 @@ -# Mail Archiver with Tailscale Sidecar Configuration - -This Docker Compose configuration sets up [Mail Archiver](https://mail-archiver.org/) with Tailscale as a sidecar container to keep the app reachable over your Tailnet. - -## Mail Archiver +# MAIL ARCHIVER [Mail Archiver](https://mail-archiver.org/) is an open-source, self-hosted email archiver. It collects mail from IMAP accounts, Microsoft 365, and imported files, and offers full-text search across message metadata and content. Pairing it with Tailscale keeps your email archive off the public internet and reachable only from devices on your Tailnet. -## Configuration Overview - -In this setup, the `tailscale` service (container `tailscale-mail-archiver`) runs Tailscale, which manages secure networking for Mail Archiver. The `application` service (container `app-mail-archiver`) uses the Tailscale network stack via Docker's `network_mode: service:tailscale` configuration. This keeps the app Tailnet-only unless you intentionally expose ports. +This stack runs Mail Archiver with a Tailscale sidecar, as described in [the standard setup](../../documentation/standard-setup.md). -The `db` service (container `app-mail-archiver-db`) runs PostgreSQL in the same network namespace, so Mail Archiver reaches it at `127.0.0.1`. The database is not exposed outside the stack. +## At a glance -## Prerequisites +| Item | Value | +| ------------- | ---------------------------------- | +| Web interface | `https://mail-archiver..ts.net` | +| Service port | `5000` | +| Image | `s1t5/mailarchiver:latest`
`postgres:17-alpine` | +| Data | `./mail-archiver-data/` | -The stack needs no extra user groups or devices beyond `/dev/net/tun` for the Tailscale container. Both containers start as root, so Docker can create the `mail-archiver-data` folders on the first start; you do not need to create them beforehand. +## Before you start -## Before the first start +Go through the [Quick Start](../../README.md#quick-start). +In addition: - Set `TS_AUTHKEY`, `APP_PW`, and `DB_PW` in `.env`. The stack does not work with empty passwords. - Use only letters and digits in `DB_PW`. The password is inserted into the database connection string, so characters such as `;` or `=` break it. - PostgreSQL applies `DB_USER` and `DB_PW` only when it creates the database on the first start. Changing them later in `.env` does not change the existing database user. - `APP_USER` and `APP_PW` define the first admin account. Log in with them after the first start. -## Access - -Tailscale Serve publishes the web interface over HTTPS at `https://mail-archiver..ts.net`. Serve proxies to Mail Archiver on port `5000`; this port is set in the `ts-serve` config in `compose.yaml` and does not follow `SERVICEPORT`. The first request can take a few seconds while Tailscale issues the certificate. - -The commented `ports:` mapping is not needed for Tailnet access. Enable it only if you also want to reach Mail Archiver on port `5000` from your local network. - -## OAuth/OIDC login (optional) - -To log in through an identity provider, set `OAUTH_SETUP=true` and fill in `OAUTH_URL`, `OAUTH_CLIENTID`, and `OAUTH_SECRET`. Use `https://mail-archiver..ts.net/oidc-signin-completed` as the redirect URI in your provider. `OAUTH_DISPLAYNAME` sets the text on the login button. Password login stays enabled, and new OAuth users need approval by an admin. - -## Volumes - -- `./mail-archiver-data/db` stores the PostgreSQL database, which holds the archived mail. -- `./mail-archiver-data/data-protection-keys` stores the keys that protect login cookies. Keeping it means users stay logged in when the container is recreated. +## Deviations from the standard setup -## Files to check +The stack adds a `db` service (`app-${SERVICE}-db`) running PostgreSQL. Mail Archiver keeps its data in a database, so it cannot run as a single container. The database listens on localhost only.The `db` service (container `app-mail-archiver-db`) runs PostgreSQL in the same network namespace, so Mail Archiver reaches it at `127.0.0.1`. The database is not exposed outside the stack. -Please check the following contents for validity as some variables need to be defined upfront. +## First run -- `.env` // Main variables `TS_AUTHKEY`, `APP_PW`, `DB_PW` +The first run can take a few seconds while Tailscale issues the certificate. Open the web interface and log in with the initial credentials, a password change will be forced for added security. -## Useful links +## Links -- [Mail Archiver website](https://mail-archiver.org/) -- [Mail Archiver on GitHub](https://github.com/s1t5/mail-archiver) -- [Mail Archiver documentation](https://github.com/s1t5/mail-archiver/tree/main/doc) +- [SERVICE documentation](https://github.com/s1t5/mail-archiver/blob/main/doc/index.md) +- [SERVICE source code](https://github.com/s1t5/mail-archiver/) diff --git a/services/mail-archiver/compose.yaml b/services/mail-archiver/compose.yaml index 01abb6d1..520907c4 100644 --- a/services/mail-archiver/compose.yaml +++ b/services/mail-archiver/compose.yaml @@ -8,8 +8,8 @@ configs: "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} services: -# Make sure you have updated/checked the .env file with the correct variables. -# All the ${ xx } need to be defined there. + # Make sure you have updated/checked the .env file with the correct variables. + # Every variable used in this file must be defined there, unless it has a default such as ${VAR:-value}. # Tailscale Sidecar Configuration tailscale: image: tailscale/tailscale:latest # Image to be used @@ -35,7 +35,7 @@ services: cap_add: - net_admin # Tailscale requirement #ports: - # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICE}PORT to the local network - may be removed if only exposure to your Tailnet is required + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICEPORT} to the local network - may be removed if only exposure to your Tailnet is required # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below #dns: # - ${DNS_SERVER} @@ -50,14 +50,14 @@ services: # ${SERVICE} application: image: ${IMAGE_URL} # Image to be used - network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale container_name: app-${SERVICE} # Name for local container management environment: # Variables are declared in .env file. # Database connection - ConnectionStrings__DefaultConnection=Host=127.0.0.1;Database=MailArchiver;Username=${DB_USER};Password=${DB_PW}; # First admin user - Authentication__Username=${APP_USER} - - Authentication__Password=${APP_PW} + - Authentication__Password=${APP_PW:?Set APP_PW in .env} # Time zone - TZ=${TZ} - TimeZone__DisplayTimeZoneId=${TZ} # Time zone used for dates in the web interface @@ -95,7 +95,7 @@ services: environment: - POSTGRES_DB=MailArchiver - POSTGRES_USER=${DB_USER} - - POSTGRES_PASSWORD=${DB_PW} + - POSTGRES_PASSWORD=${DB_PW:?Set DB_PW in .env} volumes: - ./${SERVICE}-data/db:/var/lib/postgresql/data healthcheck: @@ -105,3 +105,4 @@ services: retries: 5 # Number of retries before marking as unhealthy start_period: 30s # Time to wait before starting health checks restart: always + command: ["postgres", "-c", "listen_addresses=localhost"] From 2f27ca7d45d269839128fd0fd5d8d5679465c35f Mon Sep 17 00:00:00 2001 From: melbv Date: Sat, 17 Oct 2026 11:41:12 +0200 Subject: [PATCH 7/7] fix: lint & minor comments on comment structure --- README.md | 1 - services/mail-archiver/.env | 16 ++++++++-------- services/mail-archiver/README.md | 9 +++++---- services/mail-archiver/compose.yaml | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 71c20f51..e279a16f 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,6 @@ Every stack starts from the same [standard setup](documentation/standard-setup.m | **Traefik** | Reverse proxy and load balancer that routes requests to Docker containers by label. | [Details](services/traefik) | | **Vaultwarden** | Password manager server that works with the Bitwarden apps and browser extensions. | [Details](services/vaultwarden) | - ### Media and entertainment | Service | Description | Link | diff --git a/services/mail-archiver/.env b/services/mail-archiver/.env index e93799c6..0aed982b 100644 --- a/services/mail-archiver/.env +++ b/services/mail-archiver/.env @@ -1,21 +1,19 @@ -#version=1.1 #URL=https://github.com/tailscale-dev/ScaleTail -#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. # Service Configuration -# Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). +# Required: Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). SERVICE=mail-archiver -# Docker image URL from container registry (e.g., adguard/adguard-home). +# Required: Docker image URL from container registry (e.g., adguard/adguard-home). IMAGE_URL=s1t5/mailarchiver:latest # Network Configuration -# Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +# Required: Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. SERVICEPORT=5000 DNS_SERVER=9.9.9.9 # Tailscale Configuration -# Auth key from https://tailscale.com/admin/authkeys. +# Required: Auth key from https://tailscale.com/admin/authkeys. # See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. TS_AUTHKEY= @@ -29,13 +27,15 @@ TZ=Europe/Amsterdam # Any Container environment variables are declared below. # See https://docs.docker.com/compose/how-tos/environment-variables/ -# First admin user. Required: set a password before the first start. +# First admin user. APP_USER=admin +# Required: set a password before the first start. Generate it with: openssl rand -hex 16 APP_PW= -# Database user. Required: set a password before the first start. Use letters and digits only, because it is part of the connection string. +# Database user. # PostgreSQL applies it only when the database is first created. Existing installations must keep their current password. DB_USER=mailuser +# Required: set a password before the first start. Use letters and digits only, because it is part of the connection string. DB_PW= # OAuth/OIDC login (optional). Set OAUTH_SETUP to true and fill in the values from your identity provider. diff --git a/services/mail-archiver/README.md b/services/mail-archiver/README.md index 45960905..1a9b0e76 100644 --- a/services/mail-archiver/README.md +++ b/services/mail-archiver/README.md @@ -18,14 +18,15 @@ This stack runs Mail Archiver with a Tailscale sidecar, as described in [the sta Go through the [Quick Start](../../README.md#quick-start). In addition: -- Set `TS_AUTHKEY`, `APP_PW`, and `DB_PW` in `.env`. The stack does not work with empty passwords. + +- Set `TS_AUTHKEY`, `APP_PW`, and `DB_PW` in `.env`. Only APP_PW and DB_PW are required and Compose stops with an error until they are set. TS_AUTHKEY is only needed for the first start. - Use only letters and digits in `DB_PW`. The password is inserted into the database connection string, so characters such as `;` or `=` break it. - PostgreSQL applies `DB_USER` and `DB_PW` only when it creates the database on the first start. Changing them later in `.env` does not change the existing database user. - `APP_USER` and `APP_PW` define the first admin account. Log in with them after the first start. ## Deviations from the standard setup -The stack adds a `db` service (`app-${SERVICE}-db`) running PostgreSQL. Mail Archiver keeps its data in a database, so it cannot run as a single container. The database listens on localhost only.The `db` service (container `app-mail-archiver-db`) runs PostgreSQL in the same network namespace, so Mail Archiver reaches it at `127.0.0.1`. The database is not exposed outside the stack. +The stack adds a `db` service (`app-mail-archiver-db`) running PostgreSQL. Mail Archiver keeps its data in a database, so it cannot run as a single container. The database listens on localhost only, at `127.0.0.1`. The database is not exposed outside the stack. ## First run @@ -33,5 +34,5 @@ The first run can take a few seconds while Tailscale issues the certificate. Ope ## Links -- [SERVICE documentation](https://github.com/s1t5/mail-archiver/blob/main/doc/index.md) -- [SERVICE source code](https://github.com/s1t5/mail-archiver/) +- [Mail Archiver documentation](https://github.com/s1t5/mail-archiver/blob/main/doc/index.md) +- [Mail Archiver source code](https://github.com/s1t5/mail-archiver/) diff --git a/services/mail-archiver/compose.yaml b/services/mail-archiver/compose.yaml index 520907c4..20ec52e8 100644 --- a/services/mail-archiver/compose.yaml +++ b/services/mail-archiver/compose.yaml @@ -35,7 +35,7 @@ services: cap_add: - net_admin # Tailscale requirement #ports: - # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICEPORT} to the local network - may be removed if only exposure to your Tailnet is required + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding ports to the local network - may be removed if only exposure to your Tailnet is required # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below #dns: # - ${DNS_SERVER} @@ -47,7 +47,7 @@ services: start_period: 10s # Time to wait before starting health checks restart: always - # ${SERVICE} + # Application application: image: ${IMAGE_URL} # Image to be used network_mode: service:tailscale # Sidecar configuration to route the service through Tailscale