diff --git a/listmonk/README.md b/listmonk/README.md new file mode 100644 index 0000000..78f00d7 --- /dev/null +++ b/listmonk/README.md @@ -0,0 +1,112 @@ +# listmonk + +## Overview + +Self-hosted newsletter and mailing list manager powered by PostgreSQL. +This stack deploys listmonk with persistent storage, automatic database +initialization/upgrades, and support for reverse proxy integration. + +## Project Details + +- **Project Repository:** https://github.com/knadh/listmonk +- **Container Image:** https://hub.docker.com/r/listmonk/listmonk +- **Compose Example:** https://github.com/knadh/listmonk/blob/master/docker-compose.yml +- **Documentation:** https://listmonk.app/docs/ +- **Reverse Proxy Port:** `9000` (configurable with `LISTMONK_PORT`) + +## Environment Variable Notes + +Most variables are optional because sane defaults are already included +in the compose file. + +### Database + + POSTGRES_IMAGE – PostgreSQL container image + POSTGRES_RESTART – Restart policy for PostgreSQL + POSTGRES_USER – PostgreSQL username + POSTGRES_PASSWORD – PostgreSQL password + POSTGRES_DB – PostgreSQL database name + +### listmonk + + LISTMONK_IMAGE – listmonk container image + LISTMONK_RESTART – Restart policy for listmonk + LISTMONK_PORT – Web UI / proxy port + LISTMONK_HOSTNAME – Container hostname + LISTMONK_SSLMODE – PostgreSQL SSL mode + LISTMONK_MAX_OPEN – Maximum open DB connections + LISTMONK_MAX_IDLE – Maximum idle DB connections + LISTMONK_MAX_LIFETIME – DB connection lifetime + +### Initial Admin User + +These are only used during first startup. + + LISTMONK_ADMIN_USER – Initial admin username + LISTMONK_ADMIN_PASSWORD – Initial admin password + +### General + + VOL_PATH – Base path for persistent storage + TZ – Container timezone + +## Volume Notes + + /data/listmonk/db + PostgreSQL database storage + + /data/listmonk/uploads + listmonk uploads and media storage + +Back up both directories regularly. + +## Network Notes + +Requires an external Docker network named: + + proxy + +Create it manually if it does not already exist: + +```bash +docker network create proxy +``` + +Internal application traffic uses the private: + + listmonk + +bridge network. + +## Additional Notes / Gotchas + +- The stack automatically runs: + + - database installation + - schema upgrades + - application startup + + on container launch. + +- The install and upgrade commands are idempotent and safe to rerun. + +- Change all default passwords before exposing publicly. + +- Designed for use behind a reverse proxy such as Traefik, Caddy, + Nginx Proxy Manager, or SWAG. + +## Dockhand Stack, Deploy from Git + +Cookbooks Repository + + stackname: listmonk + Compose file path: listmonk/compose.yaml + Additional env file (optional): listmonk/sample.env + +Then load: + + listmonk/sample.env + +into the Environmental Variables section in Dockhand. + +Create the Stack. \ No newline at end of file diff --git a/listmonk/compose.yaml b/listmonk/compose.yaml new file mode 100644 index 0000000..ca34380 --- /dev/null +++ b/listmonk/compose.yaml @@ -0,0 +1,68 @@ +services: + listmonk-db: + image: ${POSTGRES_IMAGE:-postgres:18.3-alpine} + restart: ${POSTGRES_RESTART:-unless-stopped} + volumes: + - ${VOL_PATH:-/data}/listmonk/db:/var/lib/postgresql/data + environment: + - POSTGRES_USER=${POSTGRES_USER:-listmonk} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-securepassword} + - POSTGRES_DB=${POSTGRES_DB:-listmonk} + networks: + - listmonk + + listmonk: + image: ${LISTMONK_IMAGE:-listmonk/listmonk:latest} + restart: ${LISTMONK_RESTART:-unless-stopped} + volumes: + - ${VOL_PATH:-/data}/listmonk/uploads:/listmonk/uploads:rw + environment: + - LISTMONK_app__address=0.0.0.0:${LISTMONK_PORT:-9000} + + # Database + - LISTMONK_db__user=${POSTGRES_USER:-listmonk} + - LISTMONK_db__password=${POSTGRES_PASSWORD:-securepassword} + - LISTMONK_db__database=${POSTGRES_DB:-listmonk} + - LISTMONK_db__host=listmonk-db + - LISTMONK_db__port=5432 + - LISTMONK_db__ssl_mode=${LISTMONK_SSLMODE:-disable} + + # DB Pool + - LISTMONK_db__max_open=${LISTMONK_MAX_OPEN:-25} + - LISTMONK_db__max_idle=${LISTMONK_MAX_IDLE:-25} + - LISTMONK_db__max_lifetime=${LISTMONK_MAX_LIFETIME:-300s} + + # Timezone + - TZ=${TZ:-America/Vancouver} + + # Initial Admin User + - LISTMONK_ADMIN_USER=${LISTMONK_ADMIN_USER:-admin} + - LISTMONK_ADMIN_PASSWORD=${LISTMONK_ADMIN_PASSWORD:-changeme1234} + + ports: + - "${LISTMONK_PORT:-9000}:${LISTMONK_PORT:-9000}" + + networks: + - listmonk + - proxy + + hostname: ${LISTMONK_HOSTNAME:-listmonk.example.com} + + depends_on: + - listmonk-db + + command: + [ + sh, + -c, + "./listmonk --install --idempotent --yes --config '' && + ./listmonk --upgrade --yes --config '' && + ./listmonk --config ''" + ] + +networks: + listmonk: + driver: bridge + + proxy: + external: true \ No newline at end of file diff --git a/listmonk/sample.env b/listmonk/sample.env new file mode 100644 index 0000000..6c51d7d --- /dev/null +++ b/listmonk/sample.env @@ -0,0 +1,28 @@ +# PostgreSQL +POSTGRES_IMAGE=postgres:18.3-alpine +POSTGRES_RESTART=unless-stopped +POSTGRES_USER=listmonk +POSTGRES_PASSWORD=securepassword +POSTGRES_DB=listmonk + +# Storage +VOL_PATH=/data + +# Listmonk +LISTMONK_IMAGE=listmonk/listmonk:latest +LISTMONK_RESTART=unless-stopped +LISTMONK_PORT=9000 +LISTMONK_SSLMODE=disable +LISTMONK_MAX_OPEN=25 +LISTMONK_MAX_IDLE=25 +LISTMONK_MAX_LIFETIME=300s + +# Timezone +TZ=America/Vancouver + +# Admin user +LISTMONK_ADMIN_USER=admin +LISTMONK_ADMIN_PASSWORD=changeme1234 + +# Hostname +LISTMONK_HOSTNAME=listmonk.example.com \ No newline at end of file diff --git a/listmonk_dev/README.md b/listmonk_dev/README.md deleted file mode 100644 index afcdd8d..0000000 --- a/listmonk_dev/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# listmonk - -* [github](https://github.com/knadh/listmonk/tree/master) -* Proxy port: 9000 - diff --git a/listmonk_dev/config/config.toml b/listmonk_dev/config/config.toml deleted file mode 100644 index d38a16c..0000000 --- a/listmonk_dev/config/config.toml +++ /dev/null @@ -1,24 +0,0 @@ -[app] -# Interface and port where the app will run its webserver. The default value -# of localhost will only listen to connections from the current machine. To -# listen on all interfaces use '0.0.0.0'. To listen on the default web address -# port, use port 80 (this will require running with elevated permissions). -address = "0.0.0.0:9000" - -# Database. -[db] -host = "listmonk-db" -port = 5432 -user = "listmonk" -password = "securepassword" - -# Ensure that this database has been created in Postgres. -database = "listmonk" - -ssl_mode = "disable" -max_open = 25 -max_idle = 25 -max_lifetime = "300s" - -# Optional space separated Postgres DSN params. eg: "application_name=listmonk gssencmode=disable" -params = "" \ No newline at end of file diff --git a/listmonk_dev/docker-compose.yml b/listmonk_dev/docker-compose.yml deleted file mode 100644 index a7a9c2a..0000000 --- a/listmonk_dev/docker-compose.yml +++ /dev/null @@ -1,60 +0,0 @@ -services: - listmonk-db: - image: postgres:13-alpine - restart: unless-stopped - networks: - - listmonk - environment: - - POSTGRES_USER=${POSTGRES_USER:-listmonk} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-securepassword} - - POSTGRES_DB=${POSTGRES_DB:-listmonk} - volumes: - - listmonk-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"] - interval: 10s - timeout: 5s - retries: 6 - - listmonk-init: - image: listmonk/listmonk:latest - depends_on: - listmonk-db: - condition: service_healthy - networks: - - listmonk - volumes: - - ./config/config.toml:/listmonk/config.toml - command: [ "sh", "-c", "yes | ./listmonk --install --config /listmonk/config.toml" ] - - listmonk: - image: listmonk/listmonk:latest - restart: unless-stopped - depends_on: - listmonk-db: - condition: service_healthy - listmonk-init: - condition: service_completed_successfully - ports: - - "9000:9000" - networks: - - listmonk - volumes: - - ./config/config.toml:/listmonk/config.toml - - ./data/listmonk:/app/data - environment: - - SECRET_KEY=${SECRET_KEY} - - DB_PROVIDER=postgres - - DB_HOST=db - - DB_PORT=5432 - - DB_USER=${POSTGRES_USER:-listmonk} - - DB_PASSWORD=${POSTGRES_DB:-listmonk} - - DB_NAME=${POSTGRES_DB} - - WEB_SERVER_HOST=0.0.0.0 - - WEB_SERVER_PORT=8000 - -networks: - listmonk: - -volumes: - listmonk-data: \ No newline at end of file diff --git a/listmonk_dev/sample.env b/listmonk_dev/sample.env deleted file mode 100644 index 81057c5..0000000 --- a/listmonk_dev/sample.env +++ /dev/null @@ -1,4 +0,0 @@ -POSTGRES_USER=listmonk -POSTGRES_PASSWORD=securepassword -POSTGRES_DB=listmonk -SECRET_KEY=your_super_secret_key \ No newline at end of file