diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/README.md b/README.md index 1c968da..40244dd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,115 @@ -# tt-rss +# tt-rss Docker Compose files +Docker Compose files to spin up an instance of tt-rss. + +# How to run + +Add a `COMPOSE_ENV` file and save its location as a shell variable along with the location where this repo lives, here for example `/opt/containers/tt-rss` plus all other variables. At [env/fqdn_context.env.example](env/fqdn_context.env.example) you'll find an example environment file. + +When everything's ready start tt-rss with Docker Compose, otherwise head down to [Initial setup](#initial-setup) first. + +## Environment + +``` +export COMPOSE_DIR='/opt/containers/tt-rss' +export COMPOSE_CTX='ux_vilnius' +export COMPOSE_PROJECT='tt_rss-'"${COMPOSE_CTX}" +export COMPOSE_FILE="${COMPOSE_DIR}"'/compose.yaml' +export COMPOSE_OVERRIDE="${COMPOSE_DIR%/}"'/compose.override.yaml' +export COMPOSE_COMMON_SETTINGS="${COMPOSE_DIR%/}"'/common-settings.yaml' +export COMPOSE_ENV= +``` + +## Context + +On your deployment machine create the necessary Docker context to connect to and control the Docker daemon on whatever target host you'll be using, for example: +``` +docker context create fully.qualified.domain.name --docker 'host=ssh://root@fully.qualified.domain.name' +``` + +## Pull + +Pull images from Docker Hub verbatim. + +``` +docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --file "${COMPOSE_OVERRIDE}" --env-file "${COMPOSE_ENV}" pull +``` + +## Copy to target + +Copy images to target Docker host, that is assuming you deploy to a machine that itself has no network route to reach Docker Hub or your private registry of choice. Copying in its simplest form involves a local `docker save` and a remote `docker load`. Consider the helper mini-project [quico.space/Quico/copy-docker](https://quico.space/Quico/copy-docker) where [copy-docker.sh](https://quico.space/Quico/copy-docker/src/branch/main/copy-docker.sh) allows the following workflow: + +``` +while IFS= read -u 10 -r image; do + copy-docker "${image}" fully.qualified.domain.name +done 10< <(docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --file "${COMPOSE_OVERRIDE}" --env-file "${COMPOSE_ENV}" config | grep -Pi -- 'image:' | awk '{print $2}' | sort | uniq) +``` + +## Start + +``` +docker --context 'fully.qualified.domain.name' compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --file "${COMPOSE_OVERRIDE}" --env-file "${COMPOSE_ENV}" up --detach +``` + +## Clean up + +``` +docker --context 'fully.qualified.domain.name' system prune -af +docker system prune -af +``` + +# Initial setup + +We're assuming you run Docker Compose workloads with ZFS-based bind mounts. ZFS management, creating a zpool and setting adequate properties for its datasets is out of scope of this document. + +## Datasets + +Create ZFS datasets and set permissions as needed. + +* Parent dateset + ``` + export "$(grep -Pi -- '^CONTEXT=' "${COMPOSE_ENV}")" + zfs create -o canmount=off zpool/data/opt + zfs create -o mountpoint=/opt/docker-data zpool/data/opt/docker-data + ``` + +* Container-specific datasets + ``` + zfs create -p 'zpool/data/opt/docker-data/tt_rss-'"${COMPOSE_CTX}"'/tt_rss/config' + zfs create -p 'zpool/data/opt/docker-data/tt_rss-'"${COMPOSE_CTX}"'/tt_rss/data' + zfs create -p 'zpool/data/opt/docker-data/tt_rss-'"${COMPOSE_CTX}"'/db/data' + ``` + +* Change ownership + ``` + chown -R 1000:1000 '/opt/docker-data/tt_rss-'"${COMPOSE_CTX}"'/'* + ``` + +## Additional files + +No additional files are needed. + +Head back up to [How to run](#how-to-run). + +# Development + +## Conventional commits + +This project uses [Conventional Commits](https://www.conventionalcommits.org/) for its commit messages. + +### Commit types + +Commit _types_ besides `fix` and `feat` are: + +- `refactor`: Keeping functionality while streamlining or otherwise improving function flow +- `docs`: Documentation for project or components + +### Commit scopes + +The following _scopes_ are known for this project. A Conventional Commits commit message may optionally use one of the following scopes or none: + +- `tt-rss`: A change to how the `tt-rss` service component works +- `build`: Build-related changes such as `Dockerfile` fixes and features. +- `mount`: Volume or bind mount-related changes. +- `net`: Networking, IP addressing, routing changes +- `meta`: Affects the project's repo layout, file names etc. diff --git a/common-settings.yaml b/common-settings.yaml new file mode 100644 index 0000000..19d917f --- /dev/null +++ b/common-settings.yaml @@ -0,0 +1,11 @@ +services: + common-settings: + environment: + TZ: "${TIMEZONE:-Etc/UTC}" + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "10" + compress: "true" + restart: "${RESTARTPOLICY:-always}" diff --git a/compose.override.yaml b/compose.override.yaml new file mode 100644 index 0000000..e3b8931 --- /dev/null +++ b/compose.override.yaml @@ -0,0 +1,106 @@ +services: + db: + container_name: "tt_rss-postgres-${CONTEXT}" + networks: + tt_rss-default: + extends: + file: "${COMPOSE_COMMON_SETTINGS}" + service: 'common-settings' + volumes: + - "/opt/docker-data/tt_rss-${CONTEXT}/db/data:/var/lib/postgresql/data" + env_file: !reset [] + environment: + ADMIN_USER_ACCESS_LEVEL: "${ADMIN_USER_ACCESS_LEVEL}" + ADMIN_USER_PASS: "${ADMIN_USER_PASS}" + HTTP_PORT: "${HTTP_PORT}" + TZ: "${TIMEZONE}" + TTRSS_DB_NAME: "${TTRSS_DB_NAME}" + TTRSS_DB_PASS: "${TTRSS_DB_PASS}" + TTRSS_DB_USER: "${TTRSS_DB_USER}" + TTRSS_FORCE_ARTICLE_PURGE: "${TTRSS_FORCE_ARTICLE_PURGE}" + TTRSS_SINGLE_USER_MODE: "${TTRSS_SINGLE_USER_MODE}" + TTRSS_SELF_URL_PATH: "${TTRSS_SELF_URL_PATH}" + APP_BASE: "${APP_BASE}" + APP_WEB_ROOT: "${APP_WEB_ROOT}" + app: + container_name: "tt_rss-tt_rss-${CONTEXT}" + networks: + tt_rss-default: + extends: + file: "${COMPOSE_COMMON_SETTINGS}" + service: 'common-settings' + volumes: + - "/opt/docker-data/tt_rss-${CONTEXT}/tt_rss/data:/var/www/html" + - "/opt/docker-data/tt_rss-${CONTEXT}/tt_rss/config:/opt/tt-rss/config.d:ro" + env_file: !reset [] + environment: + ADMIN_USER_ACCESS_LEVEL: "${ADMIN_USER_ACCESS_LEVEL}" + ADMIN_USER_PASS: "${ADMIN_USER_PASS}" + HTTP_PORT: "${HTTP_PORT}" + TZ: "${TIMEZONE}" + TTRSS_DB_NAME: "${TTRSS_DB_NAME}" + TTRSS_DB_PASS: "${TTRSS_DB_PASS}" + TTRSS_DB_USER: "${TTRSS_DB_USER}" + TTRSS_FORCE_ARTICLE_PURGE: "${TTRSS_FORCE_ARTICLE_PURGE}" + TTRSS_SINGLE_USER_MODE: "${TTRSS_SINGLE_USER_MODE}" + TTRSS_SELF_URL_PATH: "${TTRSS_SELF_URL_PATH}" + APP_BASE: "${APP_BASE}" + APP_WEB_ROOT: "${APP_WEB_ROOT}" + updater: + container_name: "tt_rss-updater-${CONTEXT}" + networks: + tt_rss-default: + extends: + file: "${COMPOSE_COMMON_SETTINGS}" + service: 'common-settings' + volumes: + - "/opt/docker-data/tt_rss-${CONTEXT}/tt_rss/data:/var/www/html" + - "/opt/docker-data/tt_rss-${CONTEXT}/tt_rss/config:/opt/tt-rss/config.d:ro" + env_file: !reset [] + environment: + ADMIN_USER_ACCESS_LEVEL: "${ADMIN_USER_ACCESS_LEVEL}" + ADMIN_USER_PASS: "${ADMIN_USER_PASS}" + HTTP_PORT: "${HTTP_PORT}" + TZ: "${TIMEZONE}" + TTRSS_DB_NAME: "${TTRSS_DB_NAME}" + TTRSS_DB_PASS: "${TTRSS_DB_PASS}" + TTRSS_DB_USER: "${TTRSS_DB_USER}" + TTRSS_FORCE_ARTICLE_PURGE: "${TTRSS_FORCE_ARTICLE_PURGE}" + TTRSS_SINGLE_USER_MODE: "${TTRSS_SINGLE_USER_MODE}" + TTRSS_SELF_URL_PATH: "${TTRSS_SELF_URL_PATH}" + APP_BASE: "${APP_BASE}" + APP_WEB_ROOT: "${APP_WEB_ROOT}" + web-nginx: + container_name: "tt_rss-nginx-${CONTEXT}" + networks: + tt_rss-default: + extends: + file: "${COMPOSE_COMMON_SETTINGS}" + service: 'common-settings' + volumes: + - "/opt/docker-data/tt_rss-${CONTEXT}/tt_rss/data:/var/www/html" + env_file: !reset [] + environment: + ADMIN_USER_ACCESS_LEVEL: "${ADMIN_USER_ACCESS_LEVEL}" + ADMIN_USER_PASS: "${ADMIN_USER_PASS}" + HTTP_PORT: "${HTTP_PORT}" + TZ: "${TIMEZONE}" + TTRSS_DB_NAME: "${TTRSS_DB_NAME}" + TTRSS_DB_PASS: "${TTRSS_DB_PASS}" + TTRSS_DB_USER: "${TTRSS_DB_USER}" + TTRSS_FORCE_ARTICLE_PURGE: "${TTRSS_FORCE_ARTICLE_PURGE}" + TTRSS_SINGLE_USER_MODE: "${TTRSS_SINGLE_USER_MODE}" + TTRSS_SELF_URL_PATH: "${TTRSS_SELF_URL_PATH}" + APP_BASE: "${APP_BASE}" + APP_WEB_ROOT: "${APP_WEB_ROOT}" +volumes: !reset [] +networks: !override + tt_rss-default: + name: tt_rss-${CONTEXT} + driver: bridge + driver_opts: + com.docker.network.enable_ipv6: "false" + ipam: + driver: default + config: + - subnet: ${SUBNET} \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..91ab55e --- /dev/null +++ b/compose.yaml @@ -0,0 +1,72 @@ +# This Compose file comes from https://tt-rss.org/wiki/InstallationNotes/ last retrieved on 2025-02-11. + +version: '3' + +services: + + # see FAQ entry below if upgrading from a different PostgreSQL major version (e.g. 12 to 15): + # https://tt-rss.org/wiki/InstallationNotes/#i-got-the-updated-compose-file-above-and-now-my-database-keeps-restarting + db: + image: postgres:15-alpine + restart: unless-stopped + env_file: + - .env + environment: + - POSTGRES_USER=${TTRSS_DB_USER} + - POSTGRES_PASSWORD=${TTRSS_DB_PASS} + - POSTGRES_DB=${TTRSS_DB_NAME} + volumes: + - db:/var/lib/postgresql/data + + app: + image: cthulhoo/ttrss-fpm-pgsql-static:latest + restart: unless-stopped + env_file: + - .env + volumes: + - app:/var/www/html + - ./config.d:/opt/tt-rss/config.d:ro + depends_on: + - db + +# optional, makes weekly backups of your install +# backups: +# image: cthulhoo/ttrss-fpm-pgsql-static:latest +# restart: unless-stopped +# env_file: +# - .env +# volumes: +# - backups:/backups +# - app:/var/www/html +# depends_on: +# - db +# command: /opt/tt-rss/dcron.sh -f + + updater: + image: cthulhoo/ttrss-fpm-pgsql-static:latest + restart: unless-stopped + env_file: + - .env + volumes: + - app:/var/www/html + - ./config.d:/opt/tt-rss/config.d:ro + depends_on: + - app + command: /opt/tt-rss/updater.sh + + web-nginx: + image: cthulhoo/ttrss-web-nginx:latest + restart: unless-stopped + env_file: + - .env + ports: + - ${HTTP_PORT}:80 + volumes: + - app:/var/www/html:ro + depends_on: + - app + +volumes: + db: + app: + backups: \ No newline at end of file diff --git a/env/fqdn_context.env.example b/env/fqdn_context.env.example new file mode 100644 index 0000000..f826a89 --- /dev/null +++ b/env/fqdn_context.env.example @@ -0,0 +1,53 @@ +CONTEXT=ux_vilnius +SUBNET=172.30.95.0/24 +# RESTARTPOLICY=always +# TIMEZONE=Etc/UTC + +# Lines below come from https://tt-rss.org/wiki/InstallationNotes/ last retrieved on 2025-02-11. +# Put any local modifications here. + +# Run FPM under this UID/GID. +# OWNER_UID=1000 +# OWNER_GID=1000 + +# FPM settings. +#PHP_WORKER_MAX_CHILDREN=5 +#PHP_WORKER_MEMORY_LIMIT=256M + +# ADMIN_USER_* settings are applied on every startup. + +# Set admin user password to this value. If not set, random password +# will be generated on startup, look for it in the 'app' container logs. +#ADMIN_USER_PASS= + +# Sets admin user access level to this value. Valid values: +# -2 - forbidden to login +# -1 - readonly +# 0 - default user +# 10 - admin +#ADMIN_USER_ACCESS_LEVEL= + +# Auto create another user (in addition to built-in admin) unless it already exists. +#AUTO_CREATE_USER= +#AUTO_CREATE_USER_PASS= +#AUTO_CREATE_USER_ACCESS_LEVEL=0 + +# Default database credentials. +TTRSS_DB_USER=postgres +TTRSS_DB_NAME=postgres +TTRSS_DB_PASS=password + +# You can customize other config.php defines by setting overrides here. +# See tt-rss/.docker/app/Dockerfile for a complete list. + +# You probably shouldn't disable auth_internal unless you know what you're doing. +# TTRSS_PLUGINS=auth_internal,auth_remote +# TTRSS_SINGLE_USER_MODE=true +# TTRSS_SESSION_COOKIE_LIFETIME=2592000 +# TTRSS_FORCE_ARTICLE_PURGE=30 +# ... + +# Bind exposed port to 127.0.0.1 to run behind reverse proxy on the same host. +# If you plan to expose the container, remove "127.0.0.1:". +HTTP_PORT=127.0.0.1:8280 +#HTTP_PORT=8280 \ No newline at end of file