Notes advanced ⏱ 60 minutes

Outline: Team Knowledge Base

Beautiful wiki for teams. Real-time collaboration, Slack integration, and powerful search.

By Homelab Team • 2025-01-15
#outline#wiki#knowledge-base#team#docker

Introduction

Outline is an open-source team knowledge base and wiki designed for teams who value beautiful, fast, and collaborative documentation. Built with modern technologies, it offers real-time collaboration, intuitive organization, and a clean, distraction-free interface.

Why Choose Outline?

  • Beautiful Interface: Clean, modern design focused on content
  • Real-Time Collaboration: Multiple users editing simultaneously
  • Markdown Support: Write in markdown with live preview
  • Powerful Search: Fast full-text search across all documents
  • Collections: Organize documents into structured collections
  • Version History: Track all changes with complete history
  • Integrations: Slack, webhooks, and API access
  • Authentication: Support for various SSO providers

Use Cases

  • Team documentation and wikis
  • Product documentation
  • Company knowledge bases
  • Project documentation
  • Standard operating procedures
  • Onboarding documentation
  • Meeting notes and decisions
  • Technical specifications

Prerequisites

Before you begin, ensure you have:

  • Docker and Docker Compose installed
  • 2GB of available RAM (4GB recommended)
  • 10-20GB of disk space
  • Domain name with DNS configured
  • SSL certificate (Let’s Encrypt recommended)
  • SMTP server for email notifications
  • Authentication provider (Slack, Google, etc.) or OIDC

Required Services

Outline requires several supporting services:

  • PostgreSQL: Primary database
  • Redis: Caching and sessions
  • MinIO or S3: File storage
  • SMTP: Email notifications

Docker Compose Configuration

Complete Stack Setup

Create a directory structure:

mkdir -p ~/homelab/outline/{redis,postgres,minio}
cd ~/homelab/outline

Create a comprehensive docker-compose.yml:

version: '3.8'

services:
  outline:
    image: outlinewiki/outline:latest
    container_name: outline
    restart: unless-stopped
    depends_on:
      - postgres
      - redis
      - minio
    networks:
      - outline_network
      - proxy
    expose:
      - 3000
    environment:
      # Database
      - DATABASE_URL=postgres://outline:CHANGE_ME_PASSWORD@postgres:5432/outline
      - PGSSLMODE=disable

      # Redis
      - REDIS_URL=redis://redis:6379

      # Storage (MinIO)
      - AWS_ACCESS_KEY_ID=minio_access_key
      - AWS_SECRET_ACCESS_KEY=CHANGE_ME_MINIO_SECRET
      - AWS_REGION=us-east-1
      - AWS_S3_UPLOAD_BUCKET_URL=http://minio:9000
      - AWS_S3_UPLOAD_BUCKET_NAME=outline
      - AWS_S3_FORCE_PATH_STYLE=true
      - AWS_S3_ACL=private

      # Base URL
      - URL=https://wiki.yourdomain.com
      - PORT=3000

      # Secret keys (generate with: openssl rand -hex 32)
      - SECRET_KEY=GENERATE_SECRET_KEY_HERE
      - UTILS_SECRET=GENERATE_UTILS_SECRET_HERE

      # Authentication - Slack example
      - SLACK_CLIENT_ID=your_slack_client_id
      - SLACK_CLIENT_SECRET=your_slack_client_secret

      # Optional: Google OAuth
      # - GOOGLE_CLIENT_ID=your_google_client_id
      # - GOOGLE_CLIENT_SECRET=your_google_client_secret

      # Optional: OIDC Generic
      # - OIDC_CLIENT_ID=your_oidc_client_id
      # - OIDC_CLIENT_SECRET=your_oidc_client_secret
      # - OIDC_AUTH_URI=https://your-provider/authorize
      # - OIDC_TOKEN_URI=https://your-provider/token
      # - OIDC_USERINFO_URI=https://your-provider/userinfo
      # - OIDC_USERNAME_CLAIM=preferred_username
      # - OIDC_DISPLAY_NAME=Custom SSO
      # - OIDC_SCOPES=openid profile email

      # SMTP Settings
      - SMTP_HOST=smtp.gmail.com
      - SMTP_PORT=465
      - SMTP_USERNAME=your-[email protected]
      - SMTP_PASSWORD=your_app_password
      - [email protected]
      - SMTP_SECURE=true

      # Optional features
      - ENABLE_UPDATES=true
      - WEB_CONCURRENCY=1
      - MAXIMUM_IMPORT_SIZE=5242880
      - LOG_LEVEL=info
      - DEFAULT_LANGUAGE=en_US
      - FORCE_HTTPS=true

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.outline.rule=Host(`wiki.yourdomain.com`)"
      - "traefik.http.routers.outline.entrypoints=websecure"
      - "traefik.http.routers.outline.tls=true"
      - "traefik.http.routers.outline.tls.certresolver=letsencrypt"
      - "traefik.http.services.outline.loadbalancer.server.port=3000"
    volumes:
      - outline-data:/var/lib/outline/data

  postgres:
    image: postgres:15-alpine
    container_name: outline_postgres
    restart: unless-stopped
    networks:
      - outline_network
    environment:
      - POSTGRES_USER=outline
      - POSTGRES_PASSWORD=CHANGE_ME_PASSWORD
      - POSTGRES_DB=outline
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U outline"]
      interval: 30s
      timeout: 10s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: outline_redis
    restart: unless-stopped
    networks:
      - outline_network
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 30s
      timeout: 10s
      retries: 5

  minio:
    image: minio/minio:latest
    container_name: outline_minio
    restart: unless-stopped
    networks:
      - outline_network
    command: server /data --console-address ":9001"
    environment:
      - MINIO_ROOT_USER=minio_access_key
      - MINIO_ROOT_PASSWORD=CHANGE_ME_MINIO_SECRET
      - MINIO_BROWSER_REDIRECT_URL=https://minio.yourdomain.com
    volumes:
      - minio-data:/data
    expose:
      - 9000
      - 9001
    labels:
      - "traefik.enable=true"
      # MinIO Console
      - "traefik.http.routers.minio-console.rule=Host(`minio.yourdomain.com`)"
      - "traefik.http.routers.minio-console.entrypoints=websecure"
      - "traefik.http.routers.minio-console.tls=true"
      - "traefik.http.routers.minio-console.tls.certresolver=letsencrypt"
      - "traefik.http.routers.minio-console.service=minio-console"
      - "traefik.http.services.minio-console.loadbalancer.server.port=9001"
      # MinIO API
      - "traefik.http.routers.minio-api.rule=Host(`minio-api.yourdomain.com`)"
      - "traefik.http.routers.minio-api.entrypoints=websecure"
      - "traefik.http.routers.minio-api.tls=true"
      - "traefik.http.routers.minio-api.tls.certresolver=letsencrypt"
      - "traefik.http.routers.minio-api.service=minio-api"
      - "traefik.http.services.minio-api.loadbalancer.server.port=9000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

networks:
  outline_network:
    driver: bridge
  proxy:
    external: true

volumes:
  outline-data:
  postgres-data:
  redis-data:
  minio-data:

Environment Configuration

Create a .env file for sensitive variables:

# Database
POSTGRES_USER=outline
POSTGRES_PASSWORD=STRONG_PASSWORD_HERE
POSTGRES_DB=outline

# Redis
REDIS_URL=redis://redis:6379

# MinIO S3
MINIO_ROOT_USER=minio_access_key
MINIO_ROOT_PASSWORD=STRONG_MINIO_PASSWORD

# Outline Secrets
SECRET_KEY=GENERATE_WITH_OPENSSL_RAND_HEX_32
UTILS_SECRET=GENERATE_WITH_OPENSSL_RAND_HEX_32

# URLs
OUTLINE_URL=https://wiki.yourdomain.com
MINIO_URL=http://minio:9000

# SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
[email protected]
SMTP_PASSWORD=your_app_password
[email protected]

Generate secret keys:

openssl rand -hex 32  # For SECRET_KEY
openssl rand -hex 32  # For UTILS_SECRET

Deployment Process

Step 1: Create MinIO Bucket

Before starting Outline, create the S3 bucket:

# Start only MinIO first
docker-compose up -d minio

# Wait for MinIO to be ready
sleep 10

# Access MinIO console at https://minio.yourdomain.com
# Or create bucket via CLI:
docker exec outline_minio mc alias set myminio http://localhost:9000 minio_access_key CHANGE_ME_MINIO_SECRET
docker exec outline_minio mc mb myminio/outline
docker exec outline_minio mc policy set download myminio/outline

Step 2: Initialize Database

Start PostgreSQL and create the schema:

# Start postgres and redis
docker-compose up -d postgres redis

# Wait for services to be ready
sleep 15

# Initialize Outline database
docker-compose run --rm outline yarn db:migrate

Step 3: Start Outline

# Start all services
docker-compose up -d

# Monitor logs
docker-compose logs -f outline

Step 4: Access Outline

Navigate to your configured URL: https://wiki.yourdomain.com

Authentication Setup

Slack Authentication

  1. Go to https://api.slack.com/apps
  2. Create a new app
  3. Configure OAuth redirect: https://wiki.yourdomain.com/auth/slack.callback
  4. Add OAuth scopes: identity.basic, identity.email, identity.avatar
  5. Install to workspace
  6. Copy Client ID and Secret to environment variables

Google OAuth

  1. Go to Google Cloud Console
  2. Create OAuth 2.0 credentials
  3. Authorized redirect URI: https://wiki.yourdomain.com/auth/google.callback
  4. Copy Client ID and Secret

Generic OIDC (e.g., Authelia, Keycloak)

environment:
  - OIDC_CLIENT_ID=outline
  - OIDC_CLIENT_SECRET=your_client_secret
  - OIDC_AUTH_URI=https://auth.yourdomain.com/api/oidc/authorization
  - OIDC_TOKEN_URI=https://auth.yourdomain.com/api/oidc/token
  - OIDC_USERINFO_URI=https://auth.yourdomain.com/api/oidc/userinfo
  - OIDC_USERNAME_CLAIM=preferred_username
  - OIDC_DISPLAY_NAME=Authelia
  - OIDC_SCOPES=openid profile email

Data Import and Export

Import from Other Platforms

Outline supports importing from:

Confluence

# Export from Confluence as HTML
# Use Outline's import feature: Settings → Import → Confluence

Notion

# Export from Notion as Markdown & CSV
# Import via: Settings → Import → Notion

Google Docs

# Use Google Docs API or manual copy/paste
# Formatting is preserved

Export Options

Full Database Export

# Export all documents
docker exec outline_postgres pg_dump -U outline outline > outline_backup.sql

# Export attachments from MinIO
docker exec outline_minio mc mirror myminio/outline /backup/outline-files

Individual Document Export

  • Click document menu → Export
  • Choose format: Markdown, HTML, or PDF

API-Based Import Script

#!/bin/bash
# import-markdown-to-outline.sh

API_TOKEN="YOUR_API_TOKEN"
OUTLINE_URL="https://wiki.yourdomain.com"
COLLECTION_ID="your_collection_id"

for file in *.md; do
  title=$(basename "$file" .md)
  content=$(cat "$file")

  curl -X POST "$OUTLINE_URL/api/documents.create"     -H "Authorization: Bearer $API_TOKEN"     -H "Content-Type: application/json"     -d "{
      "title": "$title",
      "text": $(echo "$content" | jq -Rs .),
      "collectionId": "$COLLECTION_ID",
      "publish": true
    }"
done

Backup Strategies

Automated Backup Script

#!/bin/bash
# backup-outline.sh

BACKUP_DIR="/mnt/backups/outline"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# Backup PostgreSQL database
docker exec outline_postgres pg_dump -U outline outline |   gzip > "$BACKUP_DIR/outline_db_$TIMESTAMP.sql.gz"

# Backup Redis data
docker exec outline_redis redis-cli BGSAVE
sleep 5
docker cp outline_redis:/data/dump.rdb "$BACKUP_DIR/redis_$TIMESTAMP.rdb"

# Backup MinIO data (S3 files)
docker exec outline_minio mc mirror myminio/outline "$BACKUP_DIR/minio_$TIMESTAMP/"

# Create archive
tar -czf "$BACKUP_DIR/outline_complete_$TIMESTAMP.tar.gz"   "$BACKUP_DIR/outline_db_$TIMESTAMP.sql.gz"   "$BACKUP_DIR/redis_$TIMESTAMP.rdb"   "$BACKUP_DIR/minio_$TIMESTAMP/"

# Cleanup temporary files
rm -rf "$BACKUP_DIR/outline_db_$TIMESTAMP.sql.gz"        "$BACKUP_DIR/redis_$TIMESTAMP.rdb"        "$BACKUP_DIR/minio_$TIMESTAMP/"

# Remove old backups
find "$BACKUP_DIR" -name "outline_complete_*.tar.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: outline_complete_$TIMESTAMP.tar.gz"

Schedule Automatic Backups

chmod +x backup-outline.sh

# Add to crontab
crontab -e
# Daily backup at 2 AM
0 2 * * * /path/to/backup-outline.sh >> /var/log/outline-backup.log 2>&1

Restore from Backup

#!/bin/bash
# restore-outline.sh

BACKUP_FILE="$1"
TEMP_DIR="/tmp/outline_restore"

# Stop services
docker-compose down

# Extract backup
mkdir -p "$TEMP_DIR"
tar -xzf "$BACKUP_FILE" -C "$TEMP_DIR"

# Restore PostgreSQL
docker-compose up -d postgres
sleep 10
gunzip < "$TEMP_DIR"/outline_db_*.sql.gz |   docker exec -i outline_postgres psql -U outline

# Restore Redis
docker cp "$TEMP_DIR"/redis_*.rdb outline_redis:/data/dump.rdb
docker-compose restart redis

# Restore MinIO
docker-compose up -d minio
sleep 10
docker exec outline_minio mc mirror "$TEMP_DIR"/minio_*/ myminio/outline

# Cleanup
rm -rf "$TEMP_DIR"

# Start all services
docker-compose up -d

echo "Restore completed successfully"

Advanced Configuration

Custom Branding

Add your logo and customize colors:

environment:
  - TEAM_LOGO=https://yourdomain.com/logo.png
  - PRIMARY_COLOR=#4E5C6E

Webhook Integration

Set up webhooks for Slack notifications:

environment:
  - SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL

API Access

Generate an API token:

  1. User menu → Settings → API Tokens
  2. Create new token
  3. Use in requests:
curl -H "Authorization: Bearer YOUR_TOKEN"   https://wiki.yourdomain.com/api/documents.list

Rate Limiting

Configure rate limits:

environment:
  - RATE_LIMITER_ENABLED=true
  - RATE_LIMITER_REQUESTS=100
  - RATE_LIMITER_DURATION_WINDOW=60000

Troubleshooting

Common Issues

Outline won’t start

# Check all services
docker-compose ps

# View logs
docker-compose logs outline

# Common issues:
# 1. Database not ready
docker-compose logs postgres

# 2. Redis connection failed
docker-compose logs redis

# 3. MinIO bucket not created
docker exec outline_minio mc ls myminio/

Database Migration Errors

# Reset migrations
docker-compose run --rm outline yarn db:migrate:undo
docker-compose run --rm outline yarn db:migrate

# Check database connection
docker exec outline_postgres psql -U outline -d outline -c "dt"

File Upload Issues

# Verify MinIO bucket exists
docker exec outline_minio mc ls myminio/outline

# Check bucket policy
docker exec outline_minio mc policy get myminio/outline

# Test S3 connectivity from Outline
docker exec outline curl -v http://minio:9000

Authentication Problems

# Verify callback URLs match
# Outline: https://wiki.yourdomain.com/auth/slack.callback
# Must match exactly in OAuth provider

# Check environment variables
docker exec outline env | grep -E "SLACK|GOOGLE|OIDC"

# Review auth logs
docker-compose logs outline | grep -i auth

Performance Issues

# Check resource usage
docker stats

# Increase memory limits
services:
  outline:
    deploy:
      resources:
        limits:
          memory: 2G

# Optimize PostgreSQL
docker exec outline_postgres psql -U outline -d outline
# Run: VACUUM ANALYZE;

Debug Mode

Enable detailed logging:

environment:
  - LOG_LEVEL=debug
  - DEBUG=outline:*

Health Checks

Monitor service health:

# Check Outline health
curl https://wiki.yourdomain.com/_health

# Check PostgreSQL
docker exec outline_postgres pg_isready -U outline

# Check Redis
docker exec outline_redis redis-cli ping

# Check MinIO
curl http://localhost:9000/minio/health/live

Security Best Practices

Network Security

  1. Use HTTPS Only: Always enable SSL/TLS
  2. Firewall Rules: Restrict database access
  3. Secure Secrets: Use Docker secrets or vault
  4. Regular Updates: Keep all images updated

Database Security

# Strong passwords
POSTGRES_PASSWORD=$(openssl rand -base64 32)

# Limit connections
# In postgres docker-compose:
command:
  - "postgres"
  - "-c"
  - "max_connections=50"

File Security

# MinIO bucket policy - private by default
environment:
  - AWS_S3_ACL=private

Maintenance

Regular Updates

# Update all services
docker-compose pull
docker-compose down
docker-compose up -d

# Run migrations if needed
docker-compose run --rm outline yarn db:migrate

Database Maintenance

# Vacuum database monthly
docker exec outline_postgres psql -U outline -d outline -c "VACUUM ANALYZE;"

# Check database size
docker exec outline_postgres psql -U outline -d outline -c "l+"

Log Rotation

Configure log rotation:

services:
  outline:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Monitoring

Set up monitoring with Prometheus:

services:
  outline:
    environment:
      - PROMETHEUS_METRICS=true
      - PROMETHEUS_PORT=9090
    expose:
      - 9090

Tips and Best Practices

Content Organization

  1. Use Collections: Group related documents
  2. Consistent Naming: Develop naming conventions
  3. Templates: Create document templates
  4. Tags: Use sparingly but effectively
  5. Cross-linking: Link related documents

Collaboration

  1. Permissions: Set appropriate access levels
  2. Comments: Enable for team feedback
  3. Notifications: Configure email alerts
  4. Version History: Review changes regularly

Performance

  1. Image Optimization: Compress before uploading
  2. Regular Backups: Automate daily backups
  3. Database Tuning: Optimize PostgreSQL settings
  4. CDN: Use for static assets if needed

Additional Resources

Conclusion

Outline provides a modern, beautiful platform for team knowledge management. While the setup requires multiple services (PostgreSQL, Redis, MinIO), the result is a powerful, scalable wiki system with excellent collaboration features.

The key to success with Outline is proper authentication configuration, regular backups, and establishing good documentation practices within your team. Start with a simple setup, configure authentication properly, and expand features as your needs grow.

With proper maintenance and good organizational habits, Outline becomes the central hub for team knowledge, documentation, and collaboration.

Happy documenting!