Snapshot both servers: the VPS catalog moves to server/vps, the home server joins as server/home
The catalog in server/containers had drifted from the VPS. Eleven compose
files and twelve units no longer matched what runs, and two running
services, Gbrown_Agent and BillTracker, were missing. server/vps is now a
copy of the live files, and panama-server reads from it.
server/home holds the home server's services in the same shape. They still
run under Docker, so the podman units there are drafts for a later move, and
nothing installs from that tree. PostHog, Sentry and Plausible are checkouts
of upstream repositories. Only their local files are kept, next to an
UPSTREAM file naming the repository and the exact commit.
Every secret in both trees is a ${VAR} reference named in .env.example, or
CHANGE_ME in files that are not compose files. The live servers still hold
some of those values inline, so a live file can differ from its copy on
those lines alone. Existing .env.example files keep their comments, with new
keys appended at the end.
The ignore rules and the secrets contract now cover both trees, plus the
volume/ and volumes/ directories the home server uses.
This commit is contained in:
1 parent
a664cf68f5
commit
913a57f8a6
146 files changed
+5258
-354
No files matched your search
@@ -0,0 +1,26 @@
|
||||
API_RATE_LIMIT=1
|
||||
AUTH_AUTHENTIK_ID=CHANGE_ME
|
||||
AUTH_AUTHENTIK_SECRET=CHANGE_ME
|
||||
AUTH_AUTHENTIK_ISSUER="https://auth.gbrown.org/application/o/gibsend/"
|
||||
AWS_ACCESS_KEY=CHANGE_ME
|
||||
AWS_SECRET_KEY=CHANGE_ME
|
||||
AWS_DEFAULT_REGION=us-east-2
|
||||
DATABASE_URL=CHANGE_ME
|
||||
GITHUB_ID=Ov23liz34colyJed9t9S
|
||||
GITHUB_SECRET=CHANGE_ME
|
||||
MINIO_ROOT_USER=usesend
|
||||
MINIO_ROOT_PASSWORD=CHANGE_ME
|
||||
NEXTAUTH_SECRET=CHANGE_ME
|
||||
NEXT_PUBLIC_IS_CLOUD=false
|
||||
POSTGRES_USER=usesend
|
||||
POSTGRES_PASSWORD=CHANGE_ME
|
||||
POSTGRES_DB=usesend
|
||||
REDIS_URL=redis://usesend-redis:6379
|
||||
SMTP_HOST=smtp.usesend.gbrown.org
|
||||
SMTP_USER=usesend
|
||||
USESEND_PORT=3000
|
||||
USESEND_URL=https://usesend.gbrown.org
|
||||
|
||||
# Also set on the live machine
|
||||
AWS_ACCESS_KEY_ID=CHANGE_ME
|
||||
AWS_SECRET_ACCESS_KEY=CHANGE_ME
|
||||
@@ -0,0 +1,131 @@
|
||||
name: usesend
|
||||
networks:
|
||||
nginx-bridge:
|
||||
external: true
|
||||
services:
|
||||
redis:
|
||||
image: redis:7
|
||||
container_name: usesend-redis
|
||||
hostname: usesend-redis
|
||||
networks:
|
||||
- nginx-bridge
|
||||
env_file: .env
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: 'true'
|
||||
volumes:
|
||||
- ./volumes/redis:/data:Z
|
||||
command:
|
||||
- redis-server
|
||||
- --maxmemory-policy
|
||||
- noeviction
|
||||
tty: true
|
||||
stdin_open: true
|
||||
restart: unless-stopped
|
||||
minio:
|
||||
image: minio/minio
|
||||
container_name: usesend-storage
|
||||
hostname: usesend-storage
|
||||
networks:
|
||||
- nginx-bridge
|
||||
env_file: .env
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: 'true'
|
||||
volumes:
|
||||
- ./volumes/minio:/data:Z
|
||||
entrypoint: sh
|
||||
command: -c 'mkdir -p /data/unsend && minio server /data --console-address ':9001' --address ':9002''
|
||||
tty: true
|
||||
stdin_open: true
|
||||
restart: unless-stopped
|
||||
smtp-server:
|
||||
image: usesend/smtp-proxy:latest
|
||||
container_name: usesend-smtp-server
|
||||
hostname: usesend-smtp
|
||||
networks:
|
||||
nginx-bridge:
|
||||
# The proxy REQUIRES STARTTLS before it will accept AUTH, so clients must
|
||||
# reach it by a name the certificate actually covers. The wildcard is
|
||||
# *.gbrown.org, which matches ONE label -- hence smtp-usesend, not
|
||||
# smtp.usesend.
|
||||
#
|
||||
# Resolution: podman's aardvark-dns answers this name authoritatively for
|
||||
# containers ON nginx-bridge, handing back the container IP. NPM is not
|
||||
# involved at all -- it only reverse-proxies HTTP(S), and SMTP ports are
|
||||
# never published to the host, so the proxy is unreachable from outside.
|
||||
#
|
||||
# NOTE: there IS a wildcard *.gbrown.org record in Cloudflare, so this name
|
||||
# also resolves publicly (to Cloudflare, which serves nothing for it). The
|
||||
# consequence: if this container is STOPPED, aardvark-dns stops answering,
|
||||
# the lookup falls through to that public record, and clients dial
|
||||
# Cloudflare on 587 and hang until timeout instead of failing fast.
|
||||
aliases:
|
||||
# Both names resolve to this container and BOTH validate against npm-43,
|
||||
# which carries *.gbrown.org and *.usesend.gbrown.org. smtp.usesend is the
|
||||
# name UseSend's own UI advertises and is the target to standardise on;
|
||||
# smtp-usesend stays until every consumer has been moved across.
|
||||
- smtp-usesend.gbrown.org
|
||||
- smtp.usesend.gbrown.org
|
||||
env_file: .env
|
||||
environment:
|
||||
- SMTP_AUTH_USERNAME=${SMTP_USER:?err}
|
||||
- USESEND_BASE_URL=${USESEND_URL:?err}
|
||||
# The image's built-in default cert cannot complete a TLS 1.3 handshake at all
|
||||
# ("no suitable signature algorithm"), which silently made STARTTLS -- and so
|
||||
# ALL inbound SMTP -- impossible. These point it at the real wildcard cert.
|
||||
- USESEND_API_CERT_PATH=/certs/fullchain.pem
|
||||
- USESEND_API_KEY_PATH=/certs/privkey.pem
|
||||
ports:
|
||||
# Bound to the WireGuard address ONLY, never 0.0.0.0 (AGENTS.md §13.2). This is an
|
||||
# authenticated relay; putting it on the public interface would invite the internet to
|
||||
# brute-force it, and contradicts the Cloudflare-only posture in §7.
|
||||
# The `Home` wg interface sits in firewalld's `trusted` zone, so no rule is needed.
|
||||
# Rootless can bind these because net.ipv4.ip_unprivileged_port_start = 80.
|
||||
# Consumers on the home server reach it as smtp-usesend.gbrown.org (UniFi DNS maps
|
||||
# that name to 192.168.2.2) so the *.gbrown.org cert still validates by hostname.
|
||||
- "192.168.2.2:587:587"
|
||||
- "192.168.2.2:465:465"
|
||||
volumes:
|
||||
# Copied from NPM's letsencrypt store, NOT bind-mounted from it: NPM mounts that
|
||||
# tree with :Z (private SELinux category), so a second consumer would fight it.
|
||||
# TRADE-OFF: copies go stale. Refresh them when NPM renews (see certs/README).
|
||||
- ./certs:/certs:ro,Z
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: 'true'
|
||||
tty: true
|
||||
stdin_open: true
|
||||
restart: unless-stopped
|
||||
usesend:
|
||||
image: usesend/usesend:latest
|
||||
container_name: usesend
|
||||
hostname: usesend
|
||||
domainname: ${USESEND_URL:?err}
|
||||
networks:
|
||||
- nginx-bridge
|
||||
env_file: .env
|
||||
environment:
|
||||
- COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
||||
- PORT=${USESEND_PORT:-3000}
|
||||
- DATABASE_URL=${DATABASE_URL:?err}
|
||||
- NEXTAUTH_URL=${USESEND_URL:?err}
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET:?err}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:?err}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:?err}
|
||||
- AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:?err}
|
||||
- GITHUB_ID=${GITHUB_ID:?err}
|
||||
- GITHUB_SECRET=${GITHUB_SECRET:?err}
|
||||
- REDIS_URL=${REDIS_URL:?err}
|
||||
- NEXT_PUBLIC_IS_CLOUD=${NEXT_PUBLIC_IS_CLOUD:-false}
|
||||
- API_RATE_LIMIT=${API_RATE_LIMIT:-1}
|
||||
- SMTP_HOST=${SMTP_HOST:-smtp.usesend.gbrown.org}
|
||||
- SMTP_USER=${SMTP_USER:-usesend}
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: 'true'
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
tty: false
|
||||
stdin_open: false
|
||||
restart: unless-stopped
|
||||
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
RequiresMountsFor=/home/gib/Media
|
||||
Description=Podman Compose: UseSend
|
||||
After=network-online.target podman.socket
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=%h/Server/UseSend
|
||||
ExecStart=/usr/bin/podman compose up -d
|
||||
ExecStop=/usr/bin/podman compose down
|
||||
RemainAfterExit=yes
|
||||
TimeoutStartSec=900
|
||||
TimeoutStopSec=90
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Reference in new issue
Block a user