From 7fcc6f71e29b0e0866bc8cc746430064b572cb71 Mon Sep 17 00:00:00 2001 From: Kailash Nadh Date: Sun, 27 Oct 2024 15:35:28 +0530 Subject: [PATCH] Simplify and refactor docker-compose.yml and remove install scripts. - Remove "demo", "production" containers and have just one. - Remove dependency on config.toml and inline config to env vars. - Have a single idempotent instal+upgrade+run command that eliminates the need for manual upgrade steps. - Remove `.sh` install scripts. - Simplify and clean up install and update docs. --- README.md | 32 +++---- docker-compose.yml | 103 ++++++++++---------- docs/docs/content/installation.md | 128 +++++++------------------ docs/docs/content/upgrade.md | 15 +-- docs/site/layouts/index.html | 28 +++--- install-demo.sh | 38 -------- install-prod.sh | 153 ------------------------------ 7 files changed, 121 insertions(+), 376 deletions(-) delete mode 100755 install-demo.sh delete mode 100755 install-prod.sh diff --git a/README.md b/README.md index e54cdd763..d5ae8f26f 100644 --- a/README.md +++ b/README.md @@ -12,38 +12,30 @@ Visit [listmonk.app](https://listmonk.app) for more info. Check out the [**live ### Docker -The latest image is available on DockerHub at [`listmonk/listmonk:latest`](https://hub.docker.com/r/listmonk/listmonk/tags?page=1&ordering=last_updated&name=latest). Use the sample [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) to run manually or use the helper script. +The latest image is available on DockerHub at [`listmonk/listmonk:latest`](https://hub.docker.com/r/listmonk/listmonk/tags?page=1&ordering=last_updated&name=latest). +Download and use the sample [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml). -#### Demo -```bash -mkdir listmonk-demo && cd listmonk-demo -bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)" -``` - -DO NOT use this demo setup in production. +```shell +# Download the compose file to the current directory. +curl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml -#### Production - -```bash -mkdir listmonk && cd listmonk -bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)" +# Run the services in the background. +docker compose up -d ``` -Visit `http://localhost:9000`. - -**NOTE**: Always examine the contents of shell scripts before executing them. +Visit `http://localhost:9000` -See [installation docs](https://listmonk.app/docs/installation). +See [installation docs](https://listmonk.app/docs/installation) __________________ ### Binary - Download the [latest release](https://github.com/knadh/listmonk/releases) and extract the listmonk binary. -- `./listmonk --new-config` to generate config.toml. Then, edit the file. +- `./listmonk --new-config` to generate config.toml. Edit it. - `./listmonk --install` to setup the Postgres DB (or `--upgrade` to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects). -- Run `./listmonk` and visit `http://localhost:9000`. +- Run `./listmonk` and visit `http://localhost:9000` -See [installation docs](https://listmonk.app/docs/installation). +See [installation docs](https://listmonk.app/docs/installation) __________________ diff --git a/docker-compose.yml b/docker-compose.yml index 38062bcc7..25d04cf71 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,64 +1,65 @@ -# NOTE: This docker-compose.yml is an example setup. -# It is not intented to run out of the box -# and you must edit the below configurations to suit your needs. -# See https://listmonk.app/docs/installation/#manual-docker-install_1 +x-db-credentials: &db-credentials # Use the default POSTGRES_ credentials if they're available or simply default to "listmonk" + POSTGRES_USER: &db-user listmonk # for database user, password, and database name + POSTGRES_PASSWORD: &db-password listmonk + POSTGRES_DB: &db-name listmonk -x-app-defaults: &app-defaults - restart: unless-stopped - image: listmonk/listmonk:latest - ports: - - "9000:9000" - networks: - - listmonk - environment: - - TZ=Etc/UTC +services: + # listmonk app + app: + image: listmonk/listmonk:test + container_name: listmonk_app + restart: unless-stopped + ports: + - "9000:9000" # To change the externally exposed port, change to: $custom_port:9000 + networks: + - listmonk + hostname: listmonk.example.com # Recommend using FQDN for hostname + depends_on: + - db + command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"] + # --config (file) param is set to empty so that listmonk only uses the env vars (below) for config. + # --install --idempotent ensures that DB installation happens only once on an empty DB, on the first ever start. + # --upgrade automatically runs any DB migrations when a new image is pulled. -x-db-defaults: &db-defaults - image: postgres:14-alpine - ports: - - "9432:5432" - networks: - - listmonk - environment: - - POSTGRES_PASSWORD=listmonk - - POSTGRES_USER=listmonk - - POSTGRES_DB=listmonk - restart: unless-stopped - healthcheck: - test: ["CMD-SHELL", "pg_isready -U listmonk"] - interval: 10s - timeout: 5s - retries: 6 + environment: # The same params as in config.toml are passed as env vars here. + LISTMONK_app__address: 0.0.0.0:9000 + LISTMONK_db__user: *db-user + LISTMONK_db__password: *db-password + LISTMONK_db__database: *db-name + LISTMONK_db__host: listmonk_db + LISTMONK_db__port: 5432 + LISTMONK_db__ssl_mode: disable + LISTMONK_db__max_open: 25 + LISTMONK_db__max_idle: 25 + LISTMONK_db__max_lifetime: 300s + TZ: Etc/UTC + LISTMONK_ADMIN_USER: ${LISTMONK_ADMIN_USER:-} # If these (optional) are set during the first `docker compose up`, then the Super Admin user is automatically created. + LISTMONK_ADMIN_PASSWORD: ${LISTMONK_ADMIN_PASSWORD:-} # Otherwise, the user can be setup on the web app after the first visit to http://localhost:9000 + volumes: + - ./uploads:/listmonk/uploads:rw # Mount an uploads directory on the host to /listmonk/uploads inside the container. + # To use this, change directory path in Admin -> Settings -> Media to /listmonk/uploads -services: + # Postgres database db: - <<: *db-defaults + image: postgres:17-alpine container_name: listmonk_db + restart: unless-stopped + ports: + - "5432:5432" + networks: + - listmonk + environment: + <<: *db-credentials + healthcheck: + test: ["CMD-SHELL", "pg_isready -U listmonk"] + interval: 10s + timeout: 5s + retries: 6 volumes: - type: volume source: listmonk-data target: /var/lib/postgresql/data - app: - <<: *app-defaults - container_name: listmonk_app - hostname: listmonk.example.com # Recommend using FQDN for hostname - depends_on: - - db - volumes: - - ./config.toml:/listmonk/config.toml - - demo-db: - container_name: listmonk_demo_db - <<: *db-defaults - - demo-app: - <<: *app-defaults - container_name: listmonk_demo_app - command: [sh, -c, "yes | ./listmonk --install --config config-demo.toml && ./listmonk --config config-demo.toml"] - depends_on: - - demo-db - networks: listmonk: diff --git a/docs/docs/content/installation.md b/docs/docs/content/installation.md index 3d545c442..8032de590 100644 --- a/docs/docs/content/installation.md +++ b/docs/docs/content/installation.md @@ -1,97 +1,46 @@ # Installation -listmonk requires Postgres ⩾ 12 - -!!! Admin - listmonk generates and prints admin credentials to the terminal during installation. This can be copied to login to the admin dashboard and later changed. To choose a custom username and password during installation, - set the environment variables `LISTMONK_ADMIN_USER` and `LISTMONK_ADMIN_PASSWORD` during installation. +listmonk is a simple binary application that requires a Postgres database instance to run. The binary can be downloaded and run manually, or it can be run as a container with Docker compose. ## Binary 1. Download the [latest release](https://github.com/knadh/listmonk/releases) and extract the listmonk binary. `amd64` is the main one. It works for Intel and x86 CPUs. 1. `./listmonk --new-config` to generate config.toml. Edit the file. -1. `./listmonk --install` to install the tables in the Postgres DB. Copy the admin username and password from the terminal output (these can be changed from the admin UI later). To choose a custom username and password during installation, run: `LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx ./listmonk --install` -1. Run `./listmonk` and visit `http://localhost:9000`. +1. `./listmonk --install` to install the tables in the Postgres DB (⩾ 12). +1. Run `./listmonk` and visit `http://localhost:9000` to create the Super Admin user and login. + +!!! Tip + To set the Super Admin username and password during installation, set the environment variables: + `LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx ./listmonk --install` ## Docker The latest image is available on DockerHub at `listmonk/listmonk:latest` -!!! note - listmonk's docs and scripts use `docker compose`, which is compatible with the latest version of docker. If you installed docker and docker-compose from your Linux distribution, you probably have an older version and will need to use the `docker-compose` command instead, or you'll need to update docker manually. [More info](https://gist.github.com/MaximilianKohler/e5158fcfe6de80a9069926a67afcae11#docker-update). - -Use the sample [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) to run listmonk and Postgres DB with `docker compose` as follows: - -### Demo - -#### Easy Docker install - -```bash -mkdir listmonk-demo && cd listmonk-demo -bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)" -``` - -#### Manual Docker install +The recommended method is to download the [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) file, customize it for your environment and then to simply run `docker compose up -d`. -```bash -wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml -docker compose up -d demo-db demo-app -``` - -!!! warning - The demo does not persist Postgres after the containers are removed. **DO NOT** use this demo setup in production. - -### Production - -#### Easy Docker install - -This setup is recommended if you want to _quickly_ setup `listmonk` in production. +```shell +# Download the compose file to the current directory. +curl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml -```bash -mkdir listmonk && cd listmonk -bash -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)" +# Run the services in the background. +docker compose up -d ``` -The above shell script performs the following actions: - -- Downloads `docker-compose.yml` and generates a `config.toml`. -- Runs a Postgres container and installs the database schema. -- Runs the `listmonk` container. +Then, visit `http://localhost:9000` to create the Super Admin user and login. -!!! note - It's recommended to examine the contents of the shell script, before running in your environment. +!!! Tip + To set the Super Admin username and password during setup, set the environment variables (only the first time): + `LISTMONK_ADMIN_USER=myuser LISTMONK_ADMIN_PASSWORD=xxxxx docker compose up -d` -#### Manual Docker install -The following workflow is recommended to setup `listmonk` manually using `docker compose`. You are encouraged to customise the contents of [`docker-compose.yml`](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) to your needs. The overall setup looks like: +------------ -- `docker compose up db` to run the Postgres DB. -- `docker compose run --rm app ./listmonk --install` to setup the DB (or `--upgrade` to upgrade an existing DB). -- Copy `config.toml.sample` to your directory and make the following changes: - - `app.address` => `0.0.0.0:9000` (Port forwarding on Docker will work only if the app is advertising on all interfaces.) - - `db.host` => `listmonk_db` (Container Name of the DB container) -- Run `docker compose up app` and visit `http://localhost:9000`. +### Mounting a custom config.toml +The docker-compose file includes all necessary listmonk configuration as environment variables, `LISTMONK_*`. +If you would like to remove those and mount a config.toml instead: -##### Mounting a custom config.toml - -To mount a local `config.toml` file, add the following section to `docker-compose.yml`: - -```yml - app: - <<: *app-defaults - depends_on: - - db - volumes: - - ./path/on/your/host/config.toml:/listmonk/config.toml -``` - -!!! note - Some common changes done inside `config.toml` for Docker based setups: - - - Change `app.address` to `0.0.0.0:9000`. - - Change `db.host` to `listmonk_db`. - -Here's a sample `config.toml` you can use: +#### 1. Save the config.toml file on the host ```toml [app] @@ -99,7 +48,7 @@ address = "0.0.0.0:9000" # Database. [db] -host = "listmonk_db" +host = "listmonk_db" # Postgres container name in the compose file. port = 5432 user = "listmonk" password = "listmonk" @@ -110,25 +59,20 @@ max_idle = 25 max_lifetime = "300s" ``` -Mount the local `config.toml` inside the container at `listmonk/config.toml`. +#### 2. Mount the config file in docker-compose.yml -!!! tip - - See [configuring with environment variables](configuration.md) for variables like `app.admin_password` and `db.password` - - Ensure that both `app` and `db` containers are in running. If the containers are not running, restart them `docker compose restart app db`. - - Refer to [this tutorial](https://yasoob.me/posts/setting-up-listmonk-opensource-newsletter-mailing/) for setting up a production instance with Docker + Nginx + LetsEncrypt SSL. - -!!! info - The example `docker-compose.yml` file works with Docker Engine 24.0.5+ and Docker Compose version v2.20.2+. - -##### Changing the port - -To change the port for listmonk: +```yaml + app: + ... + volumes: + - /path/on/your/host/config.toml:/listmonk/config.toml +``` -- Ensure no other container of listmonk app is running. You can check with `docker ps | grep listmonk`. -- Change [L11](https://github.com/knadh/listmonk/blob/master/docker-compose.yml#L11) to `custom-port:9000` Eg: `3876:9000`. This will expose the port 3876 on your local network to the container's network interface on port 9000. -- For NGINX setup, if you're running NGINX on your local machine, you can proxy_pass to the `:3876`. You can also run NGINX as a docker container within the listmonk's container (for that you need to add a service `nginx` in the docker-compose.yml). If you do that, then proxy_pass will be set to `http://app:9000`. Docker's network will resolve the DNS for `app` and directly speak to port 9000 (which the app is exposing within its own network). - +#### 3. Change the `--config ''` flags in the `command:` section to point to the path +```yaml +command: [sh, -c, "./listmonk --install --idempotent --yes --config /listmonk/config.toml && ./listmonk --upgrade --yes --config /listmonk/config.toml && ./listmonk --config /listmonk/config.toml"] +``` ## Compiling from source @@ -137,13 +81,13 @@ To compile the latest unreleased version (`master` branch): 1. Make sure `go`, `nodejs`, and `yarn` are installed on your system. 2. `git clone git@github.com:knadh/listmonk.git` -3. `cd listmonk && make dist`. This will generate the `listmonk binary`. +3. `cd listmonk && make dist`. This will generate the `listmonk` binary. ## Release candidate (RC) The `master` branch with bleeding edge changes is periodically built and published as `listmonk/listmonk:rc` on DockerHub. To run the latest pre-release version, replace all instances of `listmonk/listmonk:latest` with `listmonk/listmonk:rc` in the docker-compose.yml file and follow the Docker installation steps above. While it is generally safe to run release candidate versions, they may have issues that only get resolved in a general release. -## Helm chart for kubernetes +## Helm chart for Kubernetes ![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0](https://img.shields.io/badge/AppVersion-3.0.0-informational?style=flat-square) diff --git a/docs/docs/content/upgrade.md b/docs/docs/content/upgrade.md index 8c7ff096f..244500f72 100644 --- a/docs/docs/content/upgrade.md +++ b/docs/docs/content/upgrade.md @@ -1,21 +1,24 @@ # Upgrade -Some versions may require changes to the database. These changes or database "migrations" are applied automatically and safely, but, it is recommended to take a backup of the Postgres database before running the `--upgrade` option, especially if you have made customizations to the database tables. +!!! Warning + Always take a backup of the Postgres database before upgrading listmonk ## Binary -- Download the [latest release](https://github.com/knadh/listmonk/releases) and extract the listmonk binary. -- `./listmonk --upgrade` to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects. -- Run `./listmonk` and visit `http://localhost:9000`. +- Stop the running instance of listmonk. +- Download the [latest release](https://github.com/knadh/listmonk/releases) and extract the listmonk binary and overwrite the previous version. +- `./listmonk --upgrade` to upgrade an existing database schema. Upgrades are idempotent and running them multiple times have no side effects. +- Run `./listmonk` again. If you installed listmonk as a service, you will need to stop it before overwriting the binary. Something like `sudo systemctl stop listmonk` or `sudo service listmonk stop` should work. Then overwrite the binary with the new version, then run `./listmonk --upgrade, and `start` it back with the same commands. If it's not running as a service, `pkill -9 listmonk` will stop the listmonk process. ## Docker +> Instructions for versions above v4.x.x using the latest [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) file. +- `docker compose down app` and stop the app container. - `docker compose pull` to pull the latest version from DockerHub. -- `docker compose run --rm app ./listmonk --upgrade` to upgrade an existing DB. -- Run `docker compose up app db` and visit `http://localhost:9000`. +- `docker compose up app -d` to automatically run the upgrade and start listmonk. ## Railway - Head to your dashboard, and select your Listmonk project. diff --git a/docs/site/layouts/index.html b/docs/site/layouts/index.html index fbdf65a74..bdae7e924 100644 --- a/docs/site/layouts/index.html +++ b/docs/site/layouts/index.html @@ -41,22 +41,23 @@

Binary

{{ end }}
  • - ./listmonk --new-config to generate config.toml. Edit the file. + ./listmonk --new-config to generate config.toml. Edit it.
  • -
  • ./listmonk --install to setup the Postgres DB (⩾ v9.4) or --upgrade to upgrade an existing DB.
  • +
  • ./listmonk --install to setup the Postgres DB or --upgrade to upgrade an existing DB.
  • Run ./listmonk and visit http://localhost:9000
  • Installation docs →


    Hosting providers

    - One-click deploy on Railway + One-click deploy on Railway
    Deploy on PikaPod
    Deploy on Elestio
    Deploy on Zeabur +

    *listmonk has no affiliation with any of these providers.

    @@ -64,24 +65,19 @@

    Hosting providers

    Docker

    listmonk/listmonk:latest

    - Use the sample docker-compose.yml - to run manually or use the helper script. -

    -

    Demo

    -
    mkdir listmonk-demo && cd listmonk-demo
    -sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)"
    -

    - (DO NOT use this demo setup in production) + Download and use the sample docker-compose.yml

    -

    Production

    -
    mkdir listmonk && cd listmonk
    -sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)"
    +
    +# Download the compose file to the current directory.
    +curl -O https://github.com/knadh/listmonk/blob/master/docker-compose.yml
    +
    +# Run the services in the background.
    +docker compose up -d
    +

    Visit http://localhost:9000

    Installation docs →

    - -

    NOTE: Always examine the contents of shell scripts before executing them.

    diff --git a/install-demo.sh b/install-demo.sh deleted file mode 100755 index 953be4742..000000000 --- a/install-demo.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -set -eu - -# Listmonk demo setup using `docker compose`. -# See https://listmonk.app/docs/installation/ for detailed installation steps. - -check_dependencies() { - if ! command -v curl > /dev/null; then - echo "curl is not installed." - exit 1 - fi - - if ! command -v docker > /dev/null; then - echo "docker is not installed." - exit 1 - fi - - # Check for "docker compose" functionality. - if ! docker compose version > /dev/null 2>&1; then - echo "'docker compose' functionality is not available. Please update to a newer version of Docker. See https://docs.docker.com/engine/install/ for more details." - exit 1 - fi -} - -setup_containers() { - curl -o docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml - # Use "docker compose" instead of "docker-compose" - docker compose up -d demo-db demo-app -} - -show_output(){ - echo -e "\nListmonk is now up and running. Visit http://localhost:9000 in your browser.\n" -} - - -check_dependencies -setup_containers -show_output diff --git a/install-prod.sh b/install-prod.sh deleted file mode 100755 index 13f267ecd..000000000 --- a/install-prod.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env bash -set -eu - -# Listmonk production setup using `docker compose`. -# See https://listmonk.app/docs/installation/ for detailed installation steps. - -printf '\n' - -RED="$(tput setaf 1 2>/dev/null || printf '')" -BLUE="$(tput setaf 4 2>/dev/null || printf '')" -GREEN="$(tput setaf 2 2>/dev/null || printf '')" -NO_COLOR="$(tput sgr0 2>/dev/null || printf '')" - -info() { - printf '%s\n' "${BLUE}> ${NO_COLOR} $*" -} - -error() { - printf '%s\n' "${RED}x $*${NO_COLOR}" >&2 -} - -completed() { - printf '%s\n' "${GREEN}✓ ${NO_COLOR} $*" -} - -exists() { - command -v "$1" >/dev/null 2>&1 -} - -sed_inplace() { - local search_pattern="$1" - local replacement="$2" - local file="$3" - if [[ "$(uname)" == "Darwin" ]]; then - sed -i '' "s/${search_pattern}/${replacement}/g" "$file" - else - sed -i "s/${search_pattern}/${replacement}/g" "$file" - fi -} - -check_dependencies() { - if ! exists curl; then - error "curl is not installed." - exit 1 - fi - - if ! exists docker; then - error "docker is not installed." - exit 1 - fi - - # Check for "docker compose" functionality. - if ! docker compose version >/dev/null 2>&1; then - echo "'docker compose' functionality is not available. Please update to a newer version of Docker. See https://docs.docker.com/engine/install/ for more details." - exit 1 - fi - - if ! docker compose ls >/dev/null; then - echo "no docker access - you might need to run this script with sudo or see https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user" - exit 1 - fi -} - -check_existing_db_volume() { - info "checking for an existing docker db volume" - if docker volume inspect listmonk_listmonk-data >/dev/null 2>&1; then - error "listmonk-data volume already exists. Please use docker compose down -v to remove old volumes for a fresh setup of PostgreSQL." - exit 1 - fi -} - -download() { - curl --fail --silent --location --output "$2" "$1" -} - -is_healthy() { - info "waiting for db container to be up. retrying in 3s" - health_status="$(docker inspect -f "{{.State.Health.Status}}" "$1")" - if [ "$health_status" = "healthy" ]; then - return 0 - else - return 1 - fi -} - -is_running() { - info "checking if $1 is running" - status="$(docker inspect -f "{{.State.Status}}" "$1")" - if [ "$status" = "running" ]; then - return 0 - else - return 1 - fi -} - -generate_password(){ - echo "$(LC_ALL=C tr -dc A-Za-z0-9