From 93e69389a68f98d7de50f9859afc7422cb1b4e03 Mon Sep 17 00:00:00 2001 From: sn0w Date: Mon, 4 Jun 2018 00:11:12 +0200 Subject: [PATCH] Introduce new Apache and NGINX mode. Enhance docs. --- .env.dist | 101 +++++++++++----- pleroma.dockerfile => Dockerfile | 0 README.md | 181 ++++++++++++++++++++++------ docker-compose.m4 | 194 ++++++++++++++++++++----------- docker-config.exs | 4 +- pleroma | 122 +++++++++---------- 6 files changed, 408 insertions(+), 194 deletions(-) rename pleroma.dockerfile => Dockerfile (100%) diff --git a/.env.dist b/.env.dist index d6726cc..f663a86 100644 --- a/.env.dist +++ b/.env.dist @@ -1,53 +1,98 @@ -### Docker settings ### + +######################### +# Script settings # +######################### + +# Create a postgresql container? +SCRIPT_DEPLOY_POSTGRES=true + +# Specify the server that is used as a reverse-proxy +SCRIPT_USE_PROXY=traefik + +# Enable internal SSL support? +SCRIPT_ENABLE_SSL=false + +# The port to serve HTTP on when running in nginx/apache-mode +SCRIPT_PORT_HTTP=80 + +# The port to serve HTTPs on when running in nginx/apache-mode +SCRIPT_PORT_HTTPS=443 + +# The ip to bind to in nginx/apache-mode +SCRIPT_BIND_IP=0.0.0.0 + +######################### +# Docker settings # +######################### # The docker network to bind to. -# (Useful for reverse-proxies outside of this compose). -# (Defaults to "pleroma_docker_1") -DOCKER_NETWORK= +# In traefik-mode this should be the same network that your +# traefik-container is connected to or a network that is interconnected +# with traefik's network. In manual, apache or nginx mode this value may be empty or set to any other alphanumeric value. +# (Defaults to something like "pleroma_docker_1" if empty) +DOCKER_NETWORK=pleroma # The directory where all containers store their data. # Can be a relative path, "~/...", or absolute. -DOCKER_DATADIR= +# Named docker volumes are currently not supported. +DOCKER_DATADIR=./data -### Database settings ### -POSTGRES_DB= -POSTGRES_USER= -POSTGRES_PASSWORD= +########################### +# Database settings # +########################### -### Pleroma Settings ### +# Leave POSTGRES_IP empty unless you plan to install your own database +# Leave the POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD as-is +# unless you use your own database. -# The env to use -MIX_ENV= +POSTGRES_IP= +POSTGRES_DB=pleroma +POSTGRES_USER=pleroma +POSTGRES_PASSWORD=pleroma +PLEROMA_DB_POOL_SIZE=10 -# The git tag or branch to check out on build -PLEROMA_VERSION= +########################## +# Pleroma Settings # +########################## -# The domain/scheme/port where pleroma will be hosted -PLEROMA_URL= -PLEROMA_SCHEME= -PLEROMA_PORT= +# The environment to use (dev/prod/test) +MIX_ENV=prod + +# The git tag, revision, or branch to check out on build +PLEROMA_VERSION=develop + +# The domain/scheme where pleroma will be hosted +# URL is a bare TLD +# SCHEME is the protocol without "://" +PLEROMA_URL=example.com +PLEROMA_SCHEME=https # The seed for your secret keys # (Enter something as random as possible) +# (On linux you can try something like "dd if=/dev/urandom bs=1 count=128 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev") PLEROMA_SECRET_KEY_BASE= # The name of your instance +# (This is displayed in the top-left in pleroma-fe) PLEROMA_NAME= # Your contact info PLEROMA_ADMIN_EMAIL= -# User settings -PLEROMA_MAX_TOOT_CHARS= -PLEROMA_REGISTRATIONS_OPEN= +# How many chars a notice may have at max. +PLEROMA_MAX_NOTICE_CHARS=500 -# Media proxy -PLEROMA_MEDIA_PROXY_ENABLED= -PLEROMA_MEDIA_PROXY_REDIRECT_ON_FAILURE= -PLEROMA_MEDIA_PROXY_URL= +# Whether your instance accepts new users or not (true/false) +PLEROMA_REGISTRATIONS_OPEN=true -# DB -PLEROMA_DB_POOL_SIZE= +# Enable media proxy? +PLEROMA_MEDIA_PROXY_ENABLED=false + +# The url of your media proxy (if enabled) [with "http(s)://"] +PLEROMA_MEDIA_PROXY_URL=https://cdn.example.com + +# Redirect to source on cache fail? +PLEROMA_MEDIA_PROXY_REDIRECT_ON_FAILURE=true # Whether to enable the chat feature or not -PLEROMA_CHAT_ENABLED= +PLEROMA_CHAT_ENABLED=true diff --git a/pleroma.dockerfile b/Dockerfile similarity index 100% rename from pleroma.dockerfile rename to Dockerfile diff --git a/README.md b/README.md index cf09a43..43edfc0 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,179 @@ -# Pleroma-Docker +# Pleroma-Docker (Unofficial) [Pleroma](https://pleroma.social/) is a selfhosted social network that uses OStatus/ActivityPub. -This repository is my attempt to dockerize it for easier deployment. +This repository dockerizes it for easier deployment. + +
+ +```cpp +#include +#include + +/* + * This repository comes with ABSOLUTELY NO WARRANTY + * + * I am not responsible for burning servers, angry users, fedi drama, + * thermonuclear war, or you getting fired because your boss saw your + * NSFW posts. Please do some research if you have any concerns about included + * features or the software used by this script before using it. + * You are choosing to use this setup, and if you point the finger at me for + * messing up your instance, I will laugh at you. + */ +``` + +
## Features - 100% generic - Everything is customizable -- Everything is configurable through `.env` files - Zero special host dependencies - "It just works" -## Assumptions +## Alternatives -This repo assumes that you're using [Træfik](https://traefik.io/) as your auto-configuring reverse proxy. +If this setup is a bit overwhelming there are a lot of other great dockerfiles +or guides from the community. A few are linked below. This list is not exhaustive and not ordered. -## Tutorial +- [Angristan/dockerfiles/pleroma](https://github.com/Angristan/dockerfiles/tree/master/pleroma) +- [RX14/iscute.moe](https://github.com/RX14/kurisu.rx14.co.uk/blob/master/services/iscute.moe/pleroma/Dockerfile) +- [rysiek/docker-pleroma](https://git.pleroma.social/rysiek/docker-pleroma) -- Make sure that `m4` and `docker-compose` are installed -- Check out this repo -- Create your env file (`cp .env.dist .env`) -- Edit the env values -- Run `./pleroma build` -- Run `./pleroma run` -- ... +## Installing Pleroma + +- Clone this repository +- Copy `.env.dist` to `.env` +- Edit `.env` (see "Configuring Pleroma" section below) +- Run `./pleroma build` and `./pleroma start` - Profit! -## Building an image +## Updating Pleroma -Since this setup injects code into pleroma that moves it's configuration into the environment (ref ["The Twelve-Factor App"](https://12factor.net/)), the image is 100% reusable and can be shared/replicated across multiple hosts. To do that just run `./pleroma build` as usual and then tag your image to whatever you want. Just make sure to start the container with `env_file:` or all required `-e` pairs. +Just run `./pleroma build` again and `./pleroma start` afterwards. + +You don't need to shutdown pleroma while compiling the new release. + +Every time you run `./pleroma build` the script will fetch all upstream changes and checkout `PLEROMA_VERSION`. +This means that setting `PLEROMA_VERSION` to a branch enables rolling-release updates while setting +it to a tag or commit-hash pins the version. ## Customizing Pleroma Just add your customizations (and their folder structure) to `custom.d`.
-They will be copied into the right place when the container starts.
-You can even replace/patch pleroma's code with this, because the project is recompiled at startup. +They will be copied (*not* mounted) into the right place when the container starts.
+You can even replace/patch pleroma's code with this, because the project is recompiled at startup.
-In general: Prepending `custom.d` to pleroma's customization guides should work all the time.
+In general: Prepending `custom.d/` to pleroma's customization guides should work all the time.
Check them out in the [official pleroma wiki](https://git.pleroma.social/pleroma/pleroma/wikis/home). -Here are a few customization examples: +For example: A custom thumbnail now goes into `custom.d/priv/static/instance/thumbnail.jpeg` instead of `priv/static/instance/thumbnail.jpeg`. -- I want to have a custom thumbnail - - Save it in `custom.d/priv/static/instance/thumbnail.jpeg` +## Configuring Pleroma -- I want to change the `config.json`. - - Just modify [the template](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/static/config.json) and save it in `custom.d/priv/static/static/config.json` +pleroma-docker tries to stay out of your way as much as possible while providing +a good experience for both you and your users. It thus supports multiple +"operation modes" and quite some config variables which you can mix and match. -- I want to change the background - - Throw an image into `custom.d/priv/static/static` and then edit the config from above +This guide will explain some of the tricky `.env` file parts as detailed as possible (but you should still read the comments in there). -- I want a custom logo - - See above +#### Storing Data -- I need blobs. Give me emojis. - - Save them in `custom.d/priv/static/emoji`. Then create and/or edit `custom.d/config/custom_emoji.txt`. +Currently all data is stored in subfolders of `DOCKER_DATADIR` which will be bind-mounted into the container by docker. -- I want custom ToS - - Throw a HTML document to `custom.d/priv/static/static/terms-of-service.html` +We'll evaluate named volumes as an option in the future but they're currently not supported. -You get the gist.
-Pretty basic stuff. +#### Database (`SCRIPT_DEPLOY_POSTGRES`) + +Values: `true` / `false` + +By default pleroma-docker deploys a postgresql container and links it to pleroma's container as a zero-config data store. If you already have a postgres database or want to host postgres on a physically different machine set this value to `false`. Make sure to set the `POSTGRES_*` variables when doing that. + +#### Reverse Proxy (`SCRIPT_USE_PROXY`) + +Values: `traefik` / `nginx` / `manual` + +Pleroma is usually run behind a reverse-proxy. +Pleroma-docker gives you multiple options here. + +##### Traefik + +In traefik-mode we will generate a pleroma container with traefik labels. +These will be picked up at runtime to dynamically create a reverse-proxy +configuration. This should 'just work' if `watch=true` and `exposedByDefault=false` are set in the `[docker]` section of your `traefik.conf`. SSL will also 'just work' once you add a matching `[[acme.domains]]` entry. + +##### NGINX + +In nginx-mode we will generate a bare nginx container that is linked to the +pleroma container. The nginx container is absolutely unmodified and expects to +be configured by you. The nginx file in [Pleroma's Repository](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma.nginx) is a good starting point. + +We will mount your configs like this: +``` +custom.d/server.nginx -> /etc/nginx/nginx.conf +custom.d/vhost.nginx -> /etc/nginx/conf.d/pleroma.conf +``` + +To reach your pleroma container from inside nginx use `proxy_pass http://pleroma:4000;`. + +Set `SCRIPT_PORT_HTTP` and `SCRIPT_PORT_HTTPS` to the ports you want to listen on. +Specify the ip to bind to in `SCRIPT_BIND_IP`. These values are required. + +The container only listens on `SCRIPT_PORT_HTTPS` if `SCRIPT_ENABLE_SSL` is `true`. + +##### Apache / httpd + +Just like nginx-mode this starts an unmodified apache server that expects to be +configured by you. Again [Pleroma's Config](https://git.pleroma.social/pleroma/pleroma/blob/develop/installation/pleroma-apache.conf) is a good starting point. + +We will mount your configs like this: +``` +custom.d/server.httpd -> /usr/local/apache2/conf/httpd.conf +custom.d/vhost.httpd -> /usr/local/apache2/conf/extra/httpd-vhosts.conf +``` + +To reach your pleroma container from inside apache use `ProxyPass [loc] http://localhost:4000/`. + +Again setting `SCRIPT_PORT_HTTP`, `SCRIPT_PORT_HTTPS` and `SCRIPT_BIND_IP` is required. + +The container only listens on `SCRIPT_PORT_HTTPS` if `SCRIPT_ENABLE_SSL` is `true`. + +##### Manual + +In manual mode we do not create any reverse proxy for you. +You'll have to figure something out on your own. + +This mode also doesn't bind to any IP or port. +You'll have to forward something to the container's IP. + +#### SSL (`SCRIPT_ENABLE_SSL`) + +Values: `true` / `false` + +If you want to use SSL with your Apache or NGINX containers you'll need a +certificate. Certificates need to be placed into `custom.d` and will be +bind-mounted into the server's container at runtime. + +We will mount your certs like this: +``` +custom.d/ssl.crt -> /ssl/ssl.crt +custom.d/ssl.key -> /ssl/ssl.key +``` + +You can reference them in Apache like this: +```apache + + SSLEngine on + SSLCertificateFile "/ssl/ssl.crt" + SSLCertificateKeyFile "/ssl/ssl.key" + +``` + +And in NGINX like this: +```nginx +listen 443 ssl; +ssl_certificate /ssl/ssl.crt; +ssl_certificate_key /ssl/ssl.key; +``` + +In traefik-mode and manual-mode these files and the `SCRIPT_LOCAL_SSL` value are ignored. diff --git a/docker-compose.m4 b/docker-compose.m4 index 619b1fb..bc4109e 100644 --- a/docker-compose.m4 +++ b/docker-compose.m4 @@ -1,72 +1,136 @@ -divert(-1) -define(`upcase', `translit($1, `a-z', `A-Z')') -define(`env', `upcase($1): ${upcase($1):?upcase($1)}') -define(`env_fb', `upcase($1): ${upcase($1):-$2}') -define(`env_inline', `${upcase($1):?upcase($1)}') -define(`env_inline_fb', `${upcase($1):-$2}') -divert(1)dnl +changequote(`<', `>') -version: "3" +define(, , )>) +define(, ) +define(, ) +define(, <${upcase($1):?upcase($1)}>) +define(, <${upcase($1):-$2}>) -networks: - default: - external: - name: env_inline_fb(`docker_network', `pleroma_docker_1') +{ + "version": "3", -services: - db: - image: postgres:10.3-alpine - restart: unless-stopped - environment: - env(`postgres_db') - env(`postgres_user') - env(`postgres_password') - volumes: - - env_inline(`docker_datadir')/db:/var/lib/postgresql/data - - ./initdb.sql:/docker-entrypoint-initdb.d/pleroma.sql + ifdef(__DOCKER_NETWORK, < + "networks": { + "default": { + "external": { + "name": "DOCKER_NETWORK" + } + } + }, + >) - server: - build: - context: . - dockerfile: ./pleroma.dockerfile - args: - env(`pleroma_version') - restart: unless-stopped - links: - - db - environment: - env_fb(`mix_env', `prod') + "services": { + ifelse(__SCRIPT_DEPLOY_POSTGRES, true, < + "db": { + "image": "postgres:10.3-alpine", + "restart": "unless-stopped", + "environment": [ + "env()", + "env()", + "env()" + ], + "volumes": [ + "env_inline()/db:/var/lib/postgresql/data", + "./initdb.sql:/docker-entrypoint-initdb.d/pleroma.sql" + ] + }, + >) - env_fb(`postgres_ip', `db') - env(`postgres_db') - env(`postgres_user') - env(`postgres_password') + ifdef(<__SCRIPT_USE_PROXY>, < + ifelse( + __SCRIPT_USE_PROXY, traefik, <>, + __SCRIPT_USE_PROXY, manual, <>, + __SCRIPT_USE_PROXY, nginx, < + "proxy": { + "image": "nginx:alpine", + "ports": [ + "__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTP:__SCRIPT_PORT_HTTP"ifdef(__SCRIPT_ENABLE_SSL, <,>) + ifdef(__SCRIPT_ENABLE_SSL, <"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTPS:__SCRIPT_PORT_HTTPS">) + ], + "links": [ + "server:pleroma" + ], + "volumes": [ + "./custom.d/server.nginx:/etc/nginx/nginx.conf:ro", + "./custom.d/vhost.nginx:/etc/nginx/conf.d/pleroma.conf:ro"ifdef(__SCRIPT_ENABLE_SSL, <,>) + ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.crt:/ssl/ssl.crt:ro",>) + ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.key:/ssl/ssl.key:ro">) + ] + }, + >, __SCRIPT_USE_PROXY, apache, < + "proxy": { + "image": "amd64/apache:alpine", + "ports": [ + "__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTP:__SCRIPT_PORT_HTTP"ifdef(__SCRIPT_ENABLE_SSL, <,>) + ifdef(__SCRIPT_ENABLE_SSL, <"__SCRIPT_BIND_IP:__SCRIPT_PORT_HTTPS:__SCRIPT_PORT_HTTPS">) + ], + "links": [ + "server:pleroma" + ], + "volumes": [ + "./custom.d/server.httpd:/usr/local/apache2/conf/httpd.conf:ro", + "./custom.d/vhost.httpd:/usr/local/apache2/conf/extra/httpd-vhosts.conf:ro"ifdef(__SCRIPT_ENABLE_SSL, <,>) + ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.crt:/ssl/ssl.crt:ro",>) + ifdef(__SCRIPT_ENABLE_SSL, <"./custom.d/ssl.key:/ssl/ssl.key:ro">) + ] + }, + >, < + errprint(Invalid option __SCRIPT_USE_PROXY for ) + m4exit(<1>) + > + ) + >) - env(`pleroma_url') - env(`pleroma_scheme') - env(`pleroma_port') - env(`pleroma_secret_key_base') - env(`pleroma_name') - env(`pleroma_admin_email') - env(`pleroma_max_toot_chars') - env(`pleroma_registrations_open') - env(`pleroma_media_proxy_enabled') - env(`pleroma_media_proxy_redirect_on_failure') - env(`pleroma_media_proxy_url') - env(`pleroma_db_pool_size') - env_fb(`pleroma_uploads_path', `/uploads') - env(`pleroma_chat_enabled') - volumes: - - ./custom.d:/custom.d - - env_inline(`docker_datadir')/uploads:env_inline_fb(`pleroma_uploads_path', `/uploads') - labels: - traefik.enable: "true" - traefik.fe.port: "4000" - traefik.fe.protocol: "http" - traefik.fe.entryPoints: "http,https" - traefik.fe.frontend.rule: "Host:env_inline(`pleroma_url')" - traefik.cache.port: "4000" - traefik.cache.protocol: "http" - traefik.cache.entryPoints: "http,https" - traefik.cache.frontend.rule: "Host:env_inline(`pleroma_media_proxy_url')" + "server": { + "build": { + "context": ".", + "args": [ + "env()" + ] + }, + "restart": "unless-stopped", + "links": [ + ifelse(__SCRIPT_DEPLOY_POSTGRES, true, <"db">) + ], + "environment": [ + "env_fb(, )", + "env_fb(, )", + "env()", + "env()", + "env()", + + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env()", + "env_fb(, )" + ], + "volumes": [ + "./custom.d:/custom.d", + "env_inline()/uploads:env_inline_fb(, )" + ], + "labels": [ + ifelse(SCRIPT_USE_PROXY, traefik, < + "traefik.enable=true", + "traefik.fe.port=4000", + "traefik.fe.protocol=http", + "traefik.fe.entryPoints=http,https", + "traefik.fe.frontend.rule=Host:env_inline()", + "traefik.cache.port=4000", + "traefik.cache.protocol=http", + "traefik.cache.entryPoints=http,https", + "traefik.cache.frontend.rule=Host:env_inline()" + >) + ] + } + } +} diff --git a/docker-config.exs b/docker-config.exs index 2a7817e..9290bb9 100644 --- a/docker-config.exs +++ b/docker-config.exs @@ -18,7 +18,7 @@ config :pleroma, Pleroma.Web.Endpoint, url: [ host: Docker.env(:url), scheme: Docker.env(:scheme), - port: Docker.env(:port) + port: 4000 ], secret_key_base: Docker.env(:secret_key_base) @@ -31,7 +31,7 @@ config :pleroma, :chat, config :pleroma, :instance, name: Docker.env(:name), email: Docker.env(:admin_email), - limit: Docker.env(:max_toot_chars), + limit: Docker.env(:max_notice_chars), registrations_open: Docker.env(:registrations_open) config :pleroma, :media_proxy, diff --git a/pleroma b/pleroma index fd7c165..7ae723d 100755 --- a/pleroma +++ b/pleroma @@ -1,95 +1,85 @@ #!/bin/bash set -e +set -x -function log_generic { # $1: color, $2: prefix, $3: message # - echo -e "[$(tput setaf $1)$(tput bold)$2$(tput sgr0)] $3" -} +flags="" -function log_error { # $1: message # - log_generic 1 ERR "$1" -} - -function log_ok { # $1: message # - log_generic 2 "OK " "$1" -} - -function log_info { # $1: message # - log_generic 4 INF "$1" -} - -function print_help { +print_help() { echo " Pleroma Maintenance Script Usage: - $0 [action] [flags] + $0 [action] Actions: - start Start pleroma and sibling services - stop Stop pleroma and sibling services - restart Executes #stop and #start respectively. - logs Show the current container logs - enter Enter the pleroma container for debugging/maintenance + build Rebuild the pleroma container + + purge Remove the pleroma container and all build caches + + start / up Start pleroma and sibling services + + stop / down Stop pleroma and sibling services + + restart Executes #stop and #start respectively. + + status / ps Show the current container status + + logs Show the current container logs + + enter Enter the pleroma container for debugging/maintenance " } -function action__start { - log_info "Booting pleroma" - docker-compose up --remove-orphans -d - log_ok "Done" +render_template() { + m4 $flags docker-compose.m4 | awk 'NF' } -function action__stop { - log_info "Stopping pleroma" - docker-compose down - log_ok "Done" +docker_compose() { + render_template | docker-compose -f - "$@" } -function action__logs { - docker-compose logs -f +load_env() { + if [[ ! -f .env ]]; then + echo "Please create a .env file first" + echo "(Copy .env.dist to .env for a template)" + exit 1 + fi + + while read -r line; do + if [[ "$line" == \#* ]] || [[ -z "$line" ]]; then + continue; + fi + + export "${line?}" + flags="-D__${line?} $flags" + done < .env } -function action__build { - docker-compose pull - docker-compose build --build-arg __BUST_CACHE="$(date)" server -} - -function action__enter { - docker-compose exec server ash -} - -function prepare { - log_info "Preparing script" - m4 docker-compose.m4 > docker-compose.yml -} - -function cleanup { - log_info "Cleaning up" - [[ -f docker-compose.yml ]] && rm docker-compose.yml -} - -trap "cleanup" INT TERM EXIT +action__start() { docker_compose up --remove-orphans -d; } +action__up() { action__start; } +action__stop() { docker_compose down; } +action__down() { action__stop; } +action__restart() { action__stop; action__start; } +action__logs() { docker_compose logs -f; } +action__build() { docker_compose build --build-arg __BUST_CACHE="$(date +%s)" server; } +action__enter() { docker_compose exec server ash; } +action__status() { docker_compose ps; } +action__ps() { action__status; } +action__debug() { render_template; } +action__lint() { render_template | jq; } if [[ -z "$1" ]]; then - log_error "No action provided." print_help exit 1 fi -prepare +load_env -case "$1" in -build) action__build;; -start) action__start;; -stop) action__stop;; -restart) action__stop; action__start; ;; -logs) action__logs;; -enter) action__enter;; -*) - log_error "The action '$1' is invalid." +actions=(build update purge start up stop down restart logs enter status ps debug lint) +if [[ ${actions[*]} =~ ${1} ]]; then + "action__${1}" +else print_help exit 1 -;; -esac -shift +fi