Install Outline with docker compose and Traefik

Outline is on the path to become a good self-hosted alternative to notion for team collaboration on documentation. In this short article, we will learn how to self-host it with Docker compose and Traefik

Install Outline with docker compose and Traefik
A screenshot of the interface. Image from getoutline.com

Recently N8N presented this list of interesting workflow automation software and one of them, Outline, picked my curiosity and I decided to try it out. Here's how I deployed it on my server with docker compose and Traefik as a proxy layer.

Are you interested in how to set up traefik? Find how I configured it on my other article here

What is Outline?

On their website, https://www.getoutline.com/, outline presents itself as "Your team’s knowledge base". A tool to basically write any shared written document your organization may need for functioning correctly.

Why hosting it yourself?

If you're an individual who enjoys hosting this kind of software yourself or if you need to cut the cost of SaaS software, this tool seems like a great way to run it without committing to a paid subscription.

If you don't have a server already, Digital Ocean have a great offering with promotional credit.

Once you find that it became a fundamental tool for your business, you can cut on the management cost by picking a cloud plan hosted by outline directly.

How to install Outline?

To install outline, I opted for a docker compose installation, with secrets managed externally by doppler and pushed when running the docker compose file using doppler run -- .

Here's my docker compose file

version: "3"

x-defaults: &defaults
  environment:
    - DATABASE_URL=${DATABASE_URL:-"postgres://user:pass@postgres_outline:5432/outline"}
    - DATABASE_URL_TEST=${DATABASE_URL_TEST:-"postgres://user:pass@postgres_outline:5432/outline"}
    - DEFAULT_LANGUAGE=${DEFAULT_LANGUAGE:-"en_US"}
    - ENABLE_UPDATES=${ENABLE_UPDATES:-true}
    - FORCE_HTTPS=${FORCE_HTTPS:-true}
    - LOG_LEVEL=${LOG_LEVEL:-info}
    - MAXIMUM_IMPORT_SIZE=${MAXIMUM_IMPORT_SIZE:-5120000}
    - NODE_ENV=${NODE_ENV:-production}
    - PGSSLMODE=${PGSSLMODE:-disable}
    - PORT=${PORT:-3000}
    - PRODUCTION_SSL_DISABLED=${PRODUCTION_SSL_DISABLED:-true}
    - RATE_LIMITER_DURATION_WINDOW=${RATE_LIMITER_DURATION_WINDOW:-60}
    - RATE_LIMITER_ENABLED=${RATE_LIMITER_ENABLED:-true}
    - RATE_LIMITER_REQUESTS=${RATE_LIMITER_REQUESTS:-1000}
    - REDIS_URL=${REDIS_URL:-"redis://redis_outline:6379"}
    - SECRET_KEY=${SECRET_KEY:-"secret_key"}
    - SLACK_APP_ID=${SLACK_APP_ID:-"SLACK_APP_ID"}
    - SLACK_CLIENT_ID=${SLACK_CLIENT_ID:-"SLACK_APP_ID"}
    - SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET:-"SLACK_CLIENT_SECRET"}
    - SLACK_MESSAGE_ACTIONS=${SLACK_MESSAGE_ACTIONS:-"SLACK_MESSAGE_ACTIONS"}
    - SLACK_VERIFICATION_TOKEN=${SLACK_VERIFICATION_TOKEN:-"SLACK_VERIFICATION_TOKEN"}
    - SMTP_FROM_EMAIL=${SMTP_FROM_EMAIL:-"SMTP_FROM_EMAIL"}
    - SMTP_HOST=${SMTP_HOST:-"SMTP_HOST"}
    - SMTP_PASSWORD=${SMTP_PASSWORD:-"SMTP_HOST"}
    - SMTP_PORT=${SMTP_PORT:-465}
    - SMTP_REPLY_EMAIL=${SMTP_REPLY_EMAIL:-"SMTP_REPLY_EMAIL"}
    - SMTP_SECURE=${SMTP_SECURE:-true}
    - SMTP_USERNAME=${SMTP_USERNAME:-"SMTP_USERNAME"}
    - URL=${URL:-"URL"}
    - UTILS_SECRET=${UTILS_SECRET:-"UTILS_SECRET"}
    - WEB_CONCURRENCY=${WEB_CONCURRENCY:-1}
    - DB_TYPE=${DB_TYPE:-"postgres"}
    - DB_HOST=${DB_HOST:-"postgres_outline"}
    - DB_NAME=${DB_NAME:-"outline"}
    - DB_USER=${DB_USER:-"user"}
    - DB_PASS=${DB_PASS:-"pass"}
    - DB_DUMP_FREQ=${DB_DUMP_FREQ:-720}
    - DB_CLEANUP_TIME=${DB_CLEANUP_TIME:-"72000"}
    - CHECKSUM=${CHECKSUM:-"SHA1"}
    - COMPRESSION=${COMPRESSION:-"GZIP"}
    - SPLIT_DB=${SPLIT_DB:-true}
    - CONTAINER_ENABLE_MONITORING=${CONTAINER_ENABLE_MONITORING:-false}
    - MINIO_ROOT_USER=${MINIO_ROOT_USER:-"minioadmin"}
    - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-"minioadmin"}

services:

  outline:
    <<: *defaults
    image: docker.getoutline.com/outlinewiki/outline:0.69.2
    ports:
      - "3000:3000"
    depends_on:
      - postgres
      - redis
      - storage
    networks:
      - traefik
      - default
    labels:
      - traefik.enable=true
      - traefik.http.routers.outline-${NUMBER:-1}.rule=Host(`outline.example.com`)
      - traefik.http.routers.outline-${NUMBER:-1}.entrypoints=websecure
      - traefik.http.routers.outline-${NUMBER:-1}.service=outline-${NUMBER:-1}
      - traefik.http.routers.outline-${NUMBER:-1}.tls.certresolver=letsencrypt
      - traefik.http.services.outline-${NUMBER:-1}.loadbalancer.server.port=3000

  redis:
    image: redis
    ports:
      - "6379:6379"
    command: ["redis-server"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 30s
      retries: 3
    container_name: redis_outline

  postgres:
    image: postgres
    <<: *defaults
    ports:
      - "5432:5432"
    volumes:
      - ./data/database-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 30s
      timeout: 20s
      retries: 3

    container_name: postgres_outline

  storage:
    image: minio/minio
    container_name: minio_outline
    <<: *defaults
    entrypoint: sh
    command: -c 'minio server /data'
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - ./data/storage-data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://minio_outline:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
 
  db_backup:
    container_name: outline_db_backup
    image: tiredofit/db-backup
    depends_on:
      - postgres
    volumes:
      - ../backups/outline_danielpetrica_com/outline_db/:/backup
    <<: *defaults
    restart: always

networks:
  default:
    driver: in
    attachable: true
  traefik:
    external: true
    name: traefik

docker-compose.yml

As you can see in the x-defaults section I define all the environment variables which I then push into the different services. If you're not using doppler you can set the variables with in the same terminal before you start the dockers.

export variableName=value

Command use to configure VariableName environment variable so that the docker command can read it

Another option to load environment variables in the services is to use a .env file like how is done in the original documentation. Here's an example of how it must be https://github.com/outline/outline/blob/main/.env.sample. To load this file replace <<: *defaults for env_file: ./docker.env

The docker file uses two networks, the traefik one for the public facing endpoint and an internal one just for this dockers.

Safety

To stay safe online, traefik creates a SSL certificate and exposes your site on the domain specified on the docker labels using https.

As a recovery option, I configured the project tiredofit/db-backup to periodically save the database.

With time, it would be an intelligent thing to also save this destination folder with all the backups to another location, but I'm leaving it for another day, as time in never enough.

🦺
If you care about safety you should also care about your online safety. Please check Nord Vpn for keeping you safe online.

Additional note

In the docker compose I configured a minio service but for lack of time I'm still not using it inside outline.


Thank you for reading so far.

In this article, we've explored how to run Outline on a docker compose plus traefik stack to make hosting it easy. Let me know if you have any questions in the comment section below. Also, please ask me if you want similar guides for other software

Mastodon Mastodon