Split addy into rootless Quadlets with health checks and Traefik routing
This commit is contained in:
18 files changed
+268
-173
No files matched your search
+17
-2
@@ -1,3 +1,18 @@
|
|||||||
# Ignore instance specific configuration files
|
# Node-specific configuration and secrets
|
||||||
config.map
|
config.map
|
||||||
secrets.yml
|
secrets.yml
|
||||||
|
*.env
|
||||||
|
!*.env.example
|
||||||
|
*.conf
|
||||||
|
!redis.conf
|
||||||
|
!*.conf.example
|
||||||
|
# macOS / editors / local agent settings
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
._*
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.claude/settings.local.json
|
||||||
|
.codex/settings.local.json
|
||||||
@@ -1,3 +1,84 @@
|
|||||||
# Podman Quadlet Collection
|
# Addy.io rootless Quadlets
|
||||||
|
|
||||||
This repository contains a collection of services, modelled as podman quadlets.
|
Addy.io forwards mail through aliases. This template runs addy.io, MariaDB and Redis as independent rootless systemd services. Traefik routing is an optional node-local drop-in.
|
||||||
|
|
||||||
|
| Service | Image | Persistent storage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| addy | docker.io/anonaddy/anonaddy:latest | addy.io-data |
|
||||||
|
| addy-db | docker.io/library/mariadb:latest | addy.io-db |
|
||||||
|
| addy-redis | docker.io/library/redis:latest | None; disposable cache |
|
||||||
|
|
||||||
|
All services intentionally follow `latest` and use `AutoUpdate=registry`. Upstream releases, including incompatible database releases, are accepted automatically by policy. MariaDB uses `MARIADB_AUTO_UPGRADE=1` for upgrades supported by the image entrypoint. Image rollback does not undo database migrations.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
Clone/copy this template to `~/.config/containers/systemd/quadlet-addy.io/`. Keep actual configuration outside the repository in `~/.config/addy/`. Supplied domains and env files are examples. Never commit live configuration or secrets.
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `addy*.container` | Independent services, startup probes and health checks |
|
||||||
|
| `addy.network` | Application bridge with IPv6 enabled |
|
||||||
|
| `addy*.volume` | Existing named data volumes, preserving their names |
|
||||||
|
| `healthcheck.sh` | HTTP login endpoint and SMTP banner checks |
|
||||||
|
| `redis.conf` | Disables persistence for the disposable Redis cache |
|
||||||
|
| `*.env.example` | Non-secret runtime configuration templates |
|
||||||
|
| `rootless-network.conf.example` | Experimental pesto-backed rootless bridge forwarding |
|
||||||
|
| `addy.container.d/traefik-labels.conf.example` | Optional proxy network and routing labels |
|
||||||
|
|
||||||
|
The base app joins only `addy`. MariaDB and Redis join only `addy`, with no published ports. The app publishes SMTP on host port 25. HTTP port 8000 and Rspamd port 11334 are not published. A node-local Traefik drop-in attaches the app to `webproxy` and supplies its hostname, TLS resolver and explicit upstream port. Traefik joins `webproxy` and has no direct network connection to the database/cache.
|
||||||
|
|
||||||
|
## Host prerequisites
|
||||||
|
|
||||||
|
Use a rootless service account with subordinate UID/GID mappings and lingering enabled. Commands below run as that account unless marked otherwise. Preserve its identity and storage when migrating.
|
||||||
|
|
||||||
|
Use a Podman release supporting `rootless_port_forwarder="pasta"` and a compatible passt package supplying `pesto`. Stop all rootless workloads for the account before activating the new forwarding configuration, so its shared rootless network namespace is recreated with the pesto control socket. Copy the example to `~/.config/containers/containers.conf.d/90-pesto.conf`. The forwarding feature is experimental. Verify source IPs and a container restart on the installed versions; older passt/Podman combinations have had forwarding cleanup bugs.
|
||||||
|
|
||||||
|
For direct low-port binding, the host administrator must persist and apply `net.ipv4.ip_unprivileged_port_start=25`, for example using `/etc/sysctl.d/90-rootless-container-ports.conf`. This permits all unprivileged host users to bind unused ports 25 and higher. Application configuration and units remain under the service account's home. Allow SMTP/HTTP/HTTPS on intended firewall interfaces. Remove only matching old high-port redirections after changing listeners; check IPv4 and IPv6, runtime and permanent rules. This template does not modify the firewall.
|
||||||
|
|
||||||
|
## Initial deployment
|
||||||
|
|
||||||
|
1. Copy `addy.env.example` and `db.env.example` to `~/.config/addy/addy.env` and `~/.config/addy/db.env`. Set real domains and database/user names. Use directory mode 0700 and file mode 0600.
|
||||||
|
2. Create `addy-db-password`, `addy-app-key`, and `addy-reply-secret` in the service account's Podman store. Use `podman secret create NAME -` with input from a password manager or protected prompt. Preserve existing values during migration; do not enter real secrets as literal command arguments.
|
||||||
|
3. Configure pesto as above. Deploy Traefik and its `webproxy.network` Quadlet under the same account. Traefik uses its published image with the built-in deSEC DNS provider, the existing `desec-token` Podman secret, private ACME storage, and `exposedByDefault: false`. Its Docker provider discovers this user's containers through the local Podman API socket; no compilation is needed.
|
||||||
|
4. Copy `addy.container.d/traefik-labels.conf.example` to `addy.container.d/traefik-labels.conf` and set the real hostname. The drop-in must attach `Network=webproxy.network` AND set `traefik.docker.network=webproxy`. Labels alone cannot connect the proxy to the app. Keep live drop-ins untracked.
|
||||||
|
5. Run `systemctl --user daemon-reload`, then `systemctl --user start addy-db.service addy-redis.service`. Wait for healthy readiness, then `systemctl --user start addy.service traefik.service`.
|
||||||
|
6. Start generated Quadlet services; do not enable them. Their `[Install]` sections provide boot activation with lingering. Enable the vendor timer once: `systemctl --user enable --now podman-auto-update.timer`. Its default schedule is daily; check `systemctl --user list-timers podman-auto-update.timer`.
|
||||||
|
|
||||||
|
## Readiness and recovery
|
||||||
|
|
||||||
|
MariaDB uses `healthcheck.sh --connect --innodb_initialized`; Redis must answer PONG. The app waits for their startup jobs via Wants/After, while `Notify=healthy` gates startup on health. Wants deliberately avoids stopping the app whenever a dependency restarts.
|
||||||
|
|
||||||
|
Application health requires HTTP `/login` and a local SMTP 220 banner. These do not prove end-to-end delivery or every dependency path. Startup probes run every 5 seconds, then steady-state checks every 60 seconds. Three failed steady-state checks kill the affected container and systemd recreates it. Startup permits 15 minutes for pulls/migrations. Services retry at 30-second intervals without a permanent start-limit lockout, preserving unattended recovery and future updates. Failures appear in the journal; no external alert integration is configured.
|
||||||
|
|
||||||
|
## Migration from the old single-pod and Caddy layout
|
||||||
|
|
||||||
|
Never run old and new database containers against the same volume concurrently.
|
||||||
|
|
||||||
|
1. Record images, paths and unit configuration. Preserve the existing app encryption/reply keys and database credentials. The old `addy.io-secrets` is a kube-style multi-key secret; migrate each effective value into the three separately named secrets.
|
||||||
|
2. Populate runtime env files from existing configuration. Change `DB_HOST=addy-db` and `REDIS_HOST=addy-redis`, retaining database names/users and other settings.
|
||||||
|
3. Stop the old `addy.service` and Caddy; wait for shutdown. Move the old `.kube`, YAML/config files and Caddy Quadlets outside all Quadlet search paths for rollback. Preserve all named data volumes and Caddy certificate data.
|
||||||
|
4. Activate pesto once the shared rootless network namespace is gone. Install new units and local Traefik routing drop-in. Reload systemd, start DB/cache, then app and Traefik. Traefik replaces Caddy's public listeners and obtains its own certificate using deSEC DNS-01.
|
||||||
|
5. Verify healthy services, HTTPS, SMTP over IPv4/IPv6, preserved client addresses, auto-update labels/timer, and a restart. An app-only restart must leave the DB running.
|
||||||
|
6. Probe external-to-external SMTP relay with MAIL/RCPT only, then RSET/QUIT. Expect rejection; never send DATA.
|
||||||
|
|
||||||
|
This change intentionally installs no backup services/timers. Ensure an appropriate recovery source exists before a stateful migration. For configuration rollback, stop new services before restoring old units/config and reloading systemd. A database upgraded by `latest` may need restoration from an earlier data backup to run an older image.
|
||||||
|
|
||||||
|
## Local overrides
|
||||||
|
|
||||||
|
Use `<name>.container.d/*.conf` for node customization. `.conf.example` files are inert. Reset list keys with an empty assignment before replacing them (for example `PublishPort=`). Keep live env files, domains and secrets outside the template repo or ignored.
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl --user status addy addy-db addy-redis traefik
|
||||||
|
podman ps
|
||||||
|
podman healthcheck run addy
|
||||||
|
podman healthcheck run addy-db
|
||||||
|
podman healthcheck run addy-redis
|
||||||
|
journalctl --user -u addy.service -u addy-db.service -u addy-redis.service
|
||||||
|
systemctl --user start podman-auto-update.service
|
||||||
|
podman auto-update --dry-run
|
||||||
|
podman secret ls
|
||||||
|
```
|
||||||
|
|
||||||
|
To rotate a secret, supply its value through stdin to `podman secret create --replace NAME -`, then recreate affected containers through systemd. Updating a database secret alone does not change an existing MariaDB account password; coordinate the account update with both services. Do not regenerate encryption/reply keys as routine maintenance.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[Volume]
|
||||||
|
VolumeName=addy.io-data
|
||||||
|
Label=app=addy
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Addy MariaDB
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=docker.io/library/mariadb:latest
|
||||||
|
AutoUpdate=registry
|
||||||
|
ContainerName=addy-db
|
||||||
|
Network=addy.network
|
||||||
|
NetworkAlias=addy-db
|
||||||
|
Volume=addy-db.volume:/var/lib/mysql:Z
|
||||||
|
EnvironmentFile=%h/.config/addy/db.env
|
||||||
|
Secret=addy-db-password,type=env,target=MARIADB_PASSWORD
|
||||||
|
Environment=MARIADB_RANDOM_ROOT_PASSWORD=yes
|
||||||
|
Environment=MARIADB_AUTO_UPGRADE=1
|
||||||
|
Exec=mariadbd --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||||
|
HealthCmd=healthcheck.sh --connect --innodb_initialized
|
||||||
|
HealthInterval=60s
|
||||||
|
HealthRetries=3
|
||||||
|
HealthStartPeriod=120s
|
||||||
|
HealthTimeout=10s
|
||||||
|
HealthStartupCmd=healthcheck.sh --connect --innodb_initialized
|
||||||
|
HealthStartupInterval=5s
|
||||||
|
HealthStartupRetries=60
|
||||||
|
HealthStartupTimeout=10s
|
||||||
|
Notify=healthy
|
||||||
|
HealthOnFailure=kill
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30s
|
||||||
|
TimeoutStartSec=900
|
||||||
|
TimeoutStopSec=120
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[Volume]
|
||||||
|
VolumeName=addy.io-db
|
||||||
|
Label=app=addy
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Addy Redis cache
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=docker.io/library/redis:latest
|
||||||
|
AutoUpdate=registry
|
||||||
|
ContainerName=addy-redis
|
||||||
|
Network=addy.network
|
||||||
|
NetworkAlias=addy-redis
|
||||||
|
Volume=./redis.conf:/usr/local/etc/redis/redis.conf:ro,Z
|
||||||
|
ImageVolume=ignore
|
||||||
|
Exec=redis-server /usr/local/etc/redis/redis.conf
|
||||||
|
HealthCmd=/bin/sh -ec 'redis-cli ping | grep -qx PONG'
|
||||||
|
HealthInterval=60s
|
||||||
|
HealthRetries=3
|
||||||
|
HealthStartPeriod=15s
|
||||||
|
HealthTimeout=5s
|
||||||
|
HealthStartupCmd=/bin/sh -ec 'redis-cli ping | grep -qx PONG'
|
||||||
|
HealthStartupInterval=5s
|
||||||
|
HealthStartupRetries=12
|
||||||
|
HealthStartupTimeout=5s
|
||||||
|
Notify=healthy
|
||||||
|
HealthOnFailure=kill
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30s
|
||||||
|
TimeoutStartSec=900
|
||||||
|
TimeoutStopSec=60
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Addy email forwarding
|
||||||
|
Wants=addy-db.service addy-redis.service
|
||||||
|
After=addy-db.service addy-redis.service
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
Image=docker.io/anonaddy/anonaddy:latest
|
||||||
|
AutoUpdate=registry
|
||||||
|
ContainerName=addy
|
||||||
|
Network=addy.network
|
||||||
|
NetworkAlias=addy
|
||||||
|
Volume=addy-data.volume:/data:Z
|
||||||
|
Volume=./healthcheck.sh:/usr/local/bin/addy-healthcheck:ro,Z
|
||||||
|
EnvironmentFile=%h/.config/addy/addy.env
|
||||||
|
Secret=addy-db-password,type=env,target=DB_PASSWORD
|
||||||
|
Secret=addy-app-key,type=env,target=APP_KEY
|
||||||
|
Secret=addy-reply-secret,type=env,target=ANONADDY_SECRET
|
||||||
|
PublishPort=25:25
|
||||||
|
HealthCmd=/bin/sh /usr/local/bin/addy-healthcheck
|
||||||
|
HealthInterval=60s
|
||||||
|
HealthRetries=3
|
||||||
|
HealthStartPeriod=180s
|
||||||
|
HealthTimeout=15s
|
||||||
|
HealthStartupCmd=/bin/sh /usr/local/bin/addy-healthcheck
|
||||||
|
HealthStartupInterval=5s
|
||||||
|
HealthStartupRetries=90
|
||||||
|
HealthStartupTimeout=15s
|
||||||
|
Notify=healthy
|
||||||
|
HealthOnFailure=kill
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30s
|
||||||
|
TimeoutStartSec=900
|
||||||
|
TimeoutStopSec=120
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Copy to traefik-labels.conf on the node, then set the real hostname.
|
||||||
|
[Container]
|
||||||
|
Network=webproxy.network
|
||||||
|
Label=traefik.enable=true
|
||||||
|
Label=traefik.docker.network=webproxy
|
||||||
|
Label=traefik.http.routers.addy.rule=Host(`addy.example.com`)
|
||||||
|
Label=traefik.http.routers.addy.entrypoints=websecure
|
||||||
|
Label=traefik.http.routers.addy.tls=true
|
||||||
|
Label=traefik.http.routers.addy.tls.certresolver=letsencrypt
|
||||||
|
Label=traefik.http.routers.addy.service=addy
|
||||||
|
Label=traefik.http.services.addy.loadbalancer.server.port=8000
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
TZ=Europe/Copenhagen
|
||||||
|
PUID=1000
|
||||||
|
PGID=1000
|
||||||
|
APP_URL=https://addy.example.com
|
||||||
|
APP_DEBUG=false
|
||||||
|
DB_HOST=addy-db
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=anonaddy
|
||||||
|
DB_USERNAME=anonaddy_user
|
||||||
|
REDIS_HOST=addy-redis
|
||||||
|
REDIS_PORT=6379
|
||||||
|
ANONADDY_DOMAIN=example.com
|
||||||
|
ANONADDY_ALL_DOMAINS=example.com
|
||||||
|
ANONADDY_HOSTNAME=mail.example.com
|
||||||
|
ANONADDY_RETURN_PATH=bounces@example.com
|
||||||
|
ANONADDY_ADMIN_USERNAME=admin
|
||||||
|
ANONADDY_ENABLE_REGISTRATION=false
|
||||||
|
MAIL_FROM_NAME=addy.io
|
||||||
|
MAIL_FROM_ADDRESS=addy@example.com
|
||||||
|
# Set this to a resolver reachable from inside the container.
|
||||||
|
ANONADDY_DNS_RESOLVER=1.1.1.1
|
||||||
|
# Password and encryption/reply keys are supplied by Podman secrets.
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Addy.io Mail Service (Pod)
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Kube]
|
|
||||||
Yaml=addy.yml
|
|
||||||
AutoUpdate=registry
|
|
||||||
|
|
||||||
ConfigMap=config.map
|
|
||||||
|
|
||||||
PublishPort=2525:25
|
|
||||||
PublishPort=8000:8000
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
RestartSec=3
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Addy application network
|
||||||
|
|
||||||
|
[Network]
|
||||||
|
NetworkName=addy
|
||||||
|
IPv6=true
|
||||||
|
Label=app=addy
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
name: addy.io
|
|
||||||
spec:
|
|
||||||
restartPolicy: Always
|
|
||||||
containers:
|
|
||||||
- name: addy-db
|
|
||||||
image: docker.io/library/mariadb:12
|
|
||||||
args:
|
|
||||||
- "mariadbd"
|
|
||||||
- "--character-set-server=utf8mb4"
|
|
||||||
- "--collation-server=utf8mb4_unicode_ci"
|
|
||||||
env:
|
|
||||||
- name: MARIADB_RANDOM_ROOT_PASSWORD
|
|
||||||
value: "yes"
|
|
||||||
- name: MYSQL_DATABASE
|
|
||||||
valueFrom:
|
|
||||||
configMapKeyRef:
|
|
||||||
name: addy.io-config-shared
|
|
||||||
key: DATABASE_NAME
|
|
||||||
- name: MYSQL_USER
|
|
||||||
valueFrom:
|
|
||||||
configMapKeyRef:
|
|
||||||
name: addy.io-config-shared
|
|
||||||
key: DATABASE_USER
|
|
||||||
- name: MYSQL_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: addy.io-secrets
|
|
||||||
key: db-password
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /var/lib/mysql
|
|
||||||
name: addy-db-volume
|
|
||||||
|
|
||||||
# --- Redis Service ---
|
|
||||||
- name: addy-redis
|
|
||||||
image: docker.io/library/redis:8-alpine
|
|
||||||
|
|
||||||
# --- Addy Application ---
|
|
||||||
- name: addy-app
|
|
||||||
image: docker.io/anonaddy/anonaddy:latest
|
|
||||||
ports:
|
|
||||||
- containerPort: 25
|
|
||||||
hostPort: 2525
|
|
||||||
protocol: TCP
|
|
||||||
- containerPort: 8000
|
|
||||||
hostPort: 8000
|
|
||||||
protocol: TCP
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: addy.io-config
|
|
||||||
env:
|
|
||||||
- name: DB_HOST
|
|
||||||
value: "127.0.0.1"
|
|
||||||
- name: REDIS_HOST
|
|
||||||
value: "127.0.0.1"
|
|
||||||
|
|
||||||
- name: APP_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: addy.io-secrets
|
|
||||||
key: app-key
|
|
||||||
- name: ANONADDY_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: addy.io-secrets
|
|
||||||
key: addy-secret
|
|
||||||
|
|
||||||
# Database Configuration
|
|
||||||
- name: DB_DATABASE
|
|
||||||
valueFrom:
|
|
||||||
configMapKeyRef:
|
|
||||||
name: addy.io-config-shared
|
|
||||||
key: DATABASE_NAME
|
|
||||||
- name: DB_USERNAME
|
|
||||||
valueFrom:
|
|
||||||
configMapKeyRef:
|
|
||||||
name: addy.io-config-shared
|
|
||||||
key: DATABASE_USER
|
|
||||||
- name: DB_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: addy.io-secrets
|
|
||||||
key: db-password
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /data
|
|
||||||
name: addy-data-volume
|
|
||||||
|
|
||||||
# --- Volume Definitions ---
|
|
||||||
volumes:
|
|
||||||
- name: addy-db-volume
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: addy.io-db
|
|
||||||
- name: addy-data-volume
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: addy.io-data
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: addy.io-config-shared
|
|
||||||
data:
|
|
||||||
TZ: "Europe/Berlin"
|
|
||||||
DATABASE_NAME: "anonaddy"
|
|
||||||
DATABASE_USER: "anonaddy_user"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: addy.io-config
|
|
||||||
data:
|
|
||||||
PUID: "1000"
|
|
||||||
PGID: "1000"
|
|
||||||
MEMORY_LIMIT: "256M"
|
|
||||||
UPLOAD_MAX_SIZE: "16M"
|
|
||||||
OPCACHE_MEM_SIZE: "128"
|
|
||||||
REAL_IP_FROM: "0.0.0.0/32"
|
|
||||||
REAL_IP_HEADER: "X-Forwarded-For"
|
|
||||||
LOG_IP_VAR: "remote_addr"
|
|
||||||
APP_DEBUG: "false"
|
|
||||||
APP_URL: "http://127.0.0.1:8000"
|
|
||||||
ANONADDY_RETURN_PATH: "bounces@example.com"
|
|
||||||
ANONADDY_ADMIN_USERNAME: "addy"
|
|
||||||
ANONADDY_ENABLE_REGISTRATION: "true"
|
|
||||||
ANONADDY_DOMAIN: "example.com"
|
|
||||||
ANONADDY_ALL_DOMAINS: "example.com"
|
|
||||||
ANONADDY_HOSTNAME: "mail.example.com"
|
|
||||||
ANONADDY_DNS_RESOLVER: "127.0.0.1"
|
|
||||||
ANONADDY_LIMIT: "200"
|
|
||||||
ANONADDY_BANDWIDTH_LIMIT: "104857600"
|
|
||||||
ANONADDY_NEW_ALIAS_LIMIT: "10"
|
|
||||||
ANONADDY_ADDITIONAL_USERNAME_LIMIT: "3"
|
|
||||||
ANONADDY_DKIM_SIGNING_KEY: ""
|
|
||||||
ANONADDY_DKIM_SELECTOR: ""
|
|
||||||
MAIL_FROM_NAME: "addy.io"
|
|
||||||
MAIL_FROM_ADDRESS: "addy@example.com"
|
|
||||||
POSTFIX_DEBUG: "false"
|
|
||||||
POSTFIX_SMTPD_TLS: "false"
|
|
||||||
POSTFIX_SMTP_TLS: "false"
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
MARIADB_DATABASE=anonaddy
|
||||||
|
MARIADB_USER=anonaddy_user
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
curl --fail --silent --show-error --max-time 8 --output /dev/null http://127.0.0.1:8000/login
|
||||||
|
php -r '$s = @fsockopen("127.0.0.1", 25, $errno, $errstr, 3); if (!$s) exit(1); stream_set_timeout($s, 3); $banner = fgets($s); $ok = is_string($banner) && strncmp($banner, "220 ", 4) === 0; fwrite($s, "QUIT\r\n"); fclose($s); exit($ok ? 0 : 1);'
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
bind 0.0.0.0 ::
|
||||||
|
protected-mode no
|
||||||
|
save ""
|
||||||
|
appendonly no
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Copy to ~/.config/containers/containers.conf.d/90-pesto.conf.
|
||||||
|
# Requires Podman 6 with a compatible passt package supplying pesto.
|
||||||
|
[network]
|
||||||
|
rootless_port_forwarder="pasta"
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: addy.io-secrets
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
db-password: c3VwZXItc2VjcmV0LWRiLXBhc3M=
|
|
||||||
addy-secret: replace-me-with-a-random-string
|
|
||||||
app-key: base64:CN/JeFqgUyUMUfwgqZDMjOldkBt0ye/HM9esIH7diPU=
|
|
||||||
Reference in new issue
Block a user