Notion changed the way teams think about documentation, but it also locked them into a proprietary platform with no self-hosting option, limited data sovereignty, and pricing that scales uncomfortably as teams grow. Outline is the open-source answer: a beautiful, fast, and API-first knowledge base that gives you everything teams love about Notion's wiki features while keeping your data entirely under your control. Built with React and Node.js, Outline delivers real-time collaborative editing, nested document collections, full Markdown support, and a search experience that actually works. In this guide, we will walk through deploying Outline on an Ubuntu VPS from scratch, connecting it to the supporting services it needs, and configuring single sign-on so your team can start collaborating immediately.

MassiveGRID Ubuntu VPS includes: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · Independent CPU/RAM/storage scaling · 12 Tbps DDoS protection · 4 global datacenter locations · 100% uptime SLA · 24/7 human support rated 9.5/10

Deploy a self-managed VPS — from $1.99/mo
Need dedicated resources? — from $19.80/mo
Want fully managed hosting? — we handle everything

Why Outline Stands Out as a Knowledge Base

Outline is not just another wiki tool bolted onto a project management suite. It was purpose-built for team documentation, and that focus shows in every design decision. The editor is one of the fastest rich-text experiences available in any web application, rendering Markdown in real time as you type. Documents support nested headings, tables, code blocks with syntax highlighting, embedded images, and file attachments without ever leaving the editing flow.

Real-time collaboration is native to Outline. Multiple team members can edit the same document simultaneously with live cursors, presence indicators, and automatic conflict resolution. Changes sync instantly through WebSocket connections, making it feel as responsive as Google Docs while running entirely on your own infrastructure.

The API-first architecture means every action you can perform in the UI is available through a well-documented REST API. This opens up powerful automation possibilities: generating documents from CI/CD pipelines, syncing content from external systems, building custom integrations with Slack or other tools, and programmatically managing your entire knowledge base.

Other features that make Outline compelling include full-text search with result previewing, document templates for standardizing team processes, granular permissions at the collection and document level, Slack integration for sharing and searching documents, webhook support for triggering external workflows, and a dark mode that actually looks good.

Outline vs Notion vs BookStack: Choosing the Right Tool

Before committing to a deployment, it helps to understand where Outline fits in the landscape of team documentation tools.

Notion is the incumbent SaaS platform that most teams are migrating from. It offers an incredibly flexible block-based editor, databases, kanban boards, and calendar views. However, Notion cannot be self-hosted, stores all your data on their servers, charges per member with costs that escalate quickly for larger teams, and has experienced notable outages that left entire organizations unable to access their documentation.

BookStack is another excellent open-source option that takes a more traditional approach to documentation. It organizes content into shelves, books, and chapters — a hierarchical metaphor that works well for structured reference documentation. If your team needs a digital library more than a collaborative workspace, BookStack is worth considering. We covered its deployment in our BookStack self-hosting guide.

Outline occupies the sweet spot for teams that want a modern, Notion-like experience with the benefits of self-hosting. Its document-and-collection model is flexible enough for both structured documentation and quick collaborative notes. The real-time editing, polished UI, and deep API make it the strongest choice for teams that prioritize daily active collaboration over archival reference material.

Understanding Outline's Architecture

Outline is a multi-service application, and understanding its architecture before deployment will save you troubleshooting time later. The core application is a Node.js server that handles the web interface, API, and WebSocket connections for real-time collaboration. It depends on several external services:

This multi-service requirement is the main reason teams hesitate to self-host Outline. Each dependency needs to be properly configured and secured. The upside is that this architecture is production-proven and scales well — the same services that power small teams can handle hundreds of concurrent editors with appropriate resource allocation.

Prerequisites

You will need an Ubuntu 24.04 VPS with at least 4 vCPUs, 8 GB of RAM, and 80 GB of NVMe storage to comfortably run Outline alongside all its dependencies. A MassiveGRID VPS with these specifications provides the compute, memory, and storage headroom needed for PostgreSQL, Redis, MinIO, Authentik, and the Outline application itself to coexist without resource contention.

We will use Docker and Docker Compose to orchestrate all the services. If you have not already installed Docker on your server, follow our Docker installation guide for Ubuntu VPS to get set up. Make sure both docker and docker compose commands are available before proceeding.

You will also need a domain name pointed at your server's IP address. For this guide, we will use outline.example.com as the main application domain and minio.example.com for the object storage endpoint. Create A records for both domains before starting the deployment.

Setting Up Dependencies

While we will use Docker Compose to run everything together, it is important to understand what each dependency does and how to configure it properly.

PostgreSQL

PostgreSQL serves as Outline's primary database, storing documents as structured data with full-text search indexes. Outline requires PostgreSQL 12 or later and makes heavy use of features like JSONB columns and trigram indexes for search performance. If you want to learn more about running PostgreSQL on Ubuntu, our PostgreSQL installation guide covers the fundamentals. For this deployment, we will run PostgreSQL inside Docker with a persistent volume for data durability.

Redis

Redis handles multiple roles in the Outline stack. It manages user sessions so that login state persists across requests, coordinates WebSocket connections for real-time editing across multiple server processes, powers the background job queue for tasks like document exports and email notifications, and provides caching for frequently accessed data. Our Redis caching guide explains these concepts in depth. In the Docker Compose setup, Redis will run with append-only persistence enabled.

MinIO for S3-Compatible Object Storage

Outline stores file uploads and image attachments in S3-compatible object storage rather than on the local filesystem. This design choice enables horizontal scaling and simplifies backups. MinIO provides a self-hosted S3-compatible API that works seamlessly with Outline. If you want a deeper understanding of MinIO configuration and bucket policies, refer to our MinIO self-hosting guide. We will configure a dedicated bucket for Outline with the appropriate access policies.

Authentik for OIDC Authentication

Outline does not include built-in username/password authentication. Instead, it delegates authentication to an external OpenID Connect (OIDC) provider. This is actually a security advantage — it means your knowledge base benefits from whatever authentication features your identity provider offers, including multi-factor authentication, conditional access policies, and centralized user management. We will use Authentik as the OIDC provider. If you do not already have Authentik running, follow our Authentik self-hosting guide to deploy it. You can also use other OIDC providers like Keycloak, Google Workspace, or Azure AD if you prefer.

Creating the Docker Compose Configuration

Create a dedicated directory for the Outline deployment and set up the Docker Compose file:

mkdir -p /opt/outline && cd /opt/outline

Create the docker-compose.yml file with all required services:

version: "3.8"

services:
  outline:
    image: docker.getoutline.com/outlinewiki/outline:latest
    container_name: outline
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      - postgres
      - redis
      - minio
    volumes:
      - outline-data:/var/lib/outline/data
    networks:
      - outline-network

  postgres:
    image: postgres:16-alpine
    container_name: outline-postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: outline
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: outline
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U outline"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - outline-network

  redis:
    image: redis:7-alpine
    container_name: outline-redis
    restart: unless-stopped
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - outline-network

  minio:
    image: minio/minio:latest
    container_name: outline-minio
    restart: unless-stopped
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
      MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
    volumes:
      - minio-data:/data
    ports:
      - "127.0.0.1:9000:9000"
      - "127.0.0.1:9001:9001"
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - outline-network

  minio-createbucket:
    image: minio/mc:latest
    container_name: outline-minio-createbucket
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set myminio http://minio:9000 ${MINIO_ACCESS_KEY} ${MINIO_SECRET_KEY};
      mc mb myminio/outline-uploads --ignore-existing;
      mc anonymous set download myminio/outline-uploads/public;
      exit 0;
      "
    networks:
      - outline-network

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

networks:
  outline-network:
    driver: bridge

This configuration binds all services to 127.0.0.1 only, preventing direct external access. All traffic will flow through the Nginx reverse proxy we will configure shortly.

Configuring the Environment File

The .env file contains all the configuration that Outline and its dependencies need. Generate the required secrets first:

openssl rand -hex 32  # For SECRET_KEY
openssl rand -hex 32  # For UTILS_SECRET
openssl rand -hex 16  # For POSTGRES_PASSWORD
openssl rand -hex 16  # For REDIS_PASSWORD
openssl rand -hex 16  # For MINIO_ACCESS_KEY
openssl rand -hex 16  # For MINIO_SECRET_KEY

Create the .env file in /opt/outline/:

# Application
NODE_ENV=production
SECRET_KEY=your_generated_secret_key_here
UTILS_SECRET=your_generated_utils_secret_here
URL=https://outline.example.com
PORT=3000
FORCE_HTTPS=true

# Database
DATABASE_URL=postgres://outline:your_postgres_password@postgres:5432/outline
DATABASE_CONNECTION_POOL_MIN=2
DATABASE_CONNECTION_POOL_MAX=20
PGSSLMODE=disable

# Redis
REDIS_URL=redis://:your_redis_password@redis:6379

# Object Storage (MinIO)
AWS_ACCESS_KEY_ID=your_minio_access_key
AWS_SECRET_ACCESS_KEY=your_minio_secret_key
AWS_REGION=us-east-1
AWS_S3_UPLOAD_BUCKET_URL=https://minio.example.com
AWS_S3_UPLOAD_BUCKET_NAME=outline-uploads
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
FILE_STORAGE=s3
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400

# Authentication (OIDC via Authentik)
OIDC_CLIENT_ID=your_authentik_client_id
OIDC_CLIENT_SECRET=your_authentik_client_secret
OIDC_AUTH_URI=https://authentik.example.com/application/o/authorize/
OIDC_TOKEN_URI=https://authentik.example.com/application/o/token/
OIDC_USERINFO_URI=https://authentik.example.com/application/o/userinfo/
OIDC_LOGOUT_URI=https://authentik.example.com/application/o/outline/end-session/
OIDC_DISPLAY_NAME=Authentik
OIDC_SCOPES=openid profile email

# Dependency passwords (used by docker-compose.yml)
POSTGRES_PASSWORD=your_postgres_password
REDIS_PASSWORD=your_redis_password
MINIO_ACCESS_KEY=your_minio_access_key
MINIO_SECRET_KEY=your_minio_secret_key

# Optional
LOG_LEVEL=info
DEFAULT_LANGUAGE=en_US
ENABLE_UPDATES=true
WEB_CONCURRENCY=2

Replace all placeholder values with the secrets you generated. Make sure the passwords match between the dependency sections and the connection URLs. Secure the file so only root can read it:

chmod 600 /opt/outline/.env

Setting Up Nginx as a Reverse Proxy with SSL

Outline requires HTTPS for secure cookie handling and WebSocket connections. We will use Nginx as a reverse proxy with Let's Encrypt SSL certificates. If you need a detailed walkthrough of Nginx reverse proxy configuration, our Nginx reverse proxy guide covers everything from installation to advanced configurations.

Install Nginx and Certbot if they are not already present:

apt update && apt install -y nginx certbot python3-certbot-nginx

Create the Nginx configuration for Outline at /etc/nginx/sites-available/outline:

server {
    listen 80;
    server_name outline.example.com;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name outline.example.com;

    ssl_certificate /etc/letsencrypt/live/outline.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/outline.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 25M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

The Upgrade and Connection headers are critical — they enable WebSocket passthrough for real-time collaboration. The extended timeout values prevent Nginx from prematurely closing long-lived WebSocket connections.

Create a similar configuration for MinIO at /etc/nginx/sites-available/minio:

server {
    listen 80;
    server_name minio.example.com;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name minio.example.com;

    ssl_certificate /etc/letsencrypt/live/minio.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/minio.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 25M;
    ignore_invalid_headers off;
    proxy_buffering off;

    location / {
        proxy_pass http://127.0.0.1:9000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable both sites and obtain SSL certificates:

ln -s /etc/nginx/sites-available/outline /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/minio /etc/nginx/sites-enabled/
nginx -t
certbot --nginx -d outline.example.com -d minio.example.com
systemctl reload nginx

Configuring Authentik as the OIDC Provider

With Authentik already running (from the Authentik setup guide), you need to create an OAuth2/OpenID Connect provider and application for Outline.

Log into the Authentik admin interface and navigate to Applications → Providers. Create a new OAuth2/OpenID Provider with the following settings:

After saving, note the Client ID and Client Secret — these go into the .env file as OIDC_CLIENT_ID and OIDC_CLIENT_SECRET.

Next, navigate to Applications → Applications and create a new Application:

Make sure the application is assigned to a group or policy that includes the users who should have access to your knowledge base. By default, any Authentik user in the assigned group will be able to create an account in Outline on first login.

Launching Outline

With all dependencies configured and the environment file populated, start the entire stack:

cd /opt/outline
docker compose up -d

Monitor the startup process to ensure all services come up healthy:

docker compose logs -f outline

Outline will automatically run database migrations on first startup. You should see log messages indicating that tables are being created and indexes are being built. Once you see a message like Listening on http://0.0.0.0:3000, the application is ready.

Open https://outline.example.com in your browser. You should be presented with a login screen showing the Authentik button. Click it to authenticate through Authentik. The first user to log in will automatically become the admin of the Outline workspace.

First Login and Workspace Setup

After authenticating through Authentik, Outline will create your workspace automatically. As the first user and admin, you should take a few initial setup steps.

Navigate to Settings → Details to configure your workspace name, logo, and default settings. Set the workspace name to something descriptive for your organization. You can also configure whether new users are automatically added to default collections and what permissions they receive.

Under Settings → Security, review the session duration, allowed domains for sign-up, and whether guest sharing is enabled. For most teams, restricting sign-up to your organization's email domain and disabling public document sharing is the recommended starting point.

Under Settings → Notifications, configure email notification preferences. Outline can send notifications for document mentions, collection updates, and comments, but you will need to configure SMTP settings in the .env file first by adding variables like SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, and SMTP_FROM_EMAIL.

Real-Time Collaboration and Resource Considerations

One of Outline's strongest features is real-time collaborative editing. Multiple users can open and edit the same document simultaneously, with each person's cursor visible in a different color. Changes are synchronized through WebSocket connections with operational transformation to handle conflicts gracefully.

This real-time functionality is resource-intensive. Each active editing session maintains a persistent WebSocket connection through Nginx to the Outline server. The server coordinates changes through Redis pub/sub and writes to PostgreSQL. During heavy collaborative sessions with many users editing simultaneously, you will see increased CPU usage from the Node.js process, higher Redis throughput for pub/sub message coordination, and more frequent PostgreSQL write operations.

If your team has more than 15 to 20 concurrent editors, consider upgrading to a MassiveGRID VDS with dedicated CPU and RAM resources. Dedicated resources ensure that WebSocket connections remain responsive and database writes complete without latency spikes, even during peak collaboration periods. A VDS with 8 dedicated vCPUs and 16 GB RAM can comfortably support 50 or more simultaneous editors.

Organizing with Collections, Templates, and the API

Outline organizes documents into collections — top-level groupings that function like folders with their own permission settings. Create collections for different teams or purposes: Engineering, Product, HR Policies, Onboarding, Meeting Notes, and so on. Each collection can have different access levels, allowing you to restrict sensitive HR documents while keeping engineering documentation open to all.

Within collections, documents can be nested into hierarchies by dragging them in the sidebar. This creates a natural information architecture without requiring rigid categorization upfront. Documents can also be moved between collections as your organization evolves.

Templates are a powerful feature for standardizing how your team creates documents. Create templates for recurring document types like meeting notes, project briefs, incident reports, or architecture decision records. Templates are accessible from the new document menu and populate the editor with predefined structure, headings, and placeholder text.

The REST API opens up automation possibilities that go far beyond the web interface. Common API use cases include:

# List all documents in a collection
curl -X POST https://outline.example.com/api/documents.list \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"collectionId": "collection-uuid"}'

# Create a new document
curl -X POST https://outline.example.com/api/documents.create \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sprint 42 Retrospective",
    "text": "## What went well\n\n## What could improve\n\n## Action items\n",
    "collectionId": "collection-uuid",
    "publish": true
  }'

# Search documents
curl -X POST https://outline.example.com/api/documents.search \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query": "deployment process"}'

Generate API tokens from Settings → API in the Outline interface. You can create multiple tokens with different names for different integrations, making it easy to rotate credentials when needed.

Backup Strategy

A comprehensive backup strategy for Outline must cover all four data stores: PostgreSQL, Redis, MinIO, and Outline's local data volume.

For PostgreSQL, create regular logical backups using pg_dump:

docker exec outline-postgres pg_dump -U outline -Fc outline > /backup/outline-db-$(date +%Y%m%d).dump

For MinIO, synchronize the bucket to a backup location using the MinIO client:

docker exec outline-minio mc mirror /data/outline-uploads /backup/minio-outline-uploads

For Redis, the append-only file is automatically persisted to the Docker volume. Copying the volume periodically is sufficient for most recovery scenarios.

For a fully automated approach that handles scheduling, retention, compression, and offsite copies, follow our automated backup guide. It covers setting up cron-based backup scripts with rotation policies and optional offsite replication — exactly what a multi-service deployment like Outline needs.

It is also a good idea to periodically test your restoration process. Spin up a test instance, restore from backups, and verify that documents, attachments, and user accounts are all intact. A backup you have never tested is a backup you cannot trust.

Migrating from Notion

Outline includes built-in Notion import functionality that makes migration straightforward. The process works through Notion's export feature and Outline's import handler.

First, export your data from Notion. In Notion, go to Settings → Workspace → Export all workspace content. Select Markdown & CSV as the export format. Notion will email you a ZIP file containing all your pages as Markdown files with a folder structure that mirrors your workspace hierarchy.

In Outline, navigate to Settings → Import and select Notion as the source. Upload the ZIP file directly. Outline will parse the Markdown files, recreate the document hierarchy as nested collections, and import inline images and file attachments. The import process handles most Markdown formatting correctly, including headings, lists, code blocks, tables, and links.

Some Notion-specific features will not transfer perfectly. Notion databases, kanban boards, calendar views, and advanced blocks like synced blocks or embedded databases have no direct equivalent in Outline. These will either be converted to simple tables or skipped during import. Review imported documents after migration and manually fix any content that did not convert cleanly.

For large workspaces with thousands of documents, the import can take several minutes. Monitor the import progress in the Outline interface and avoid making other changes until the process completes. After import, verify that document links between pages still work — Outline attempts to preserve internal links but some may need manual correction.

Ongoing Maintenance

Outline receives regular updates with new features, bug fixes, and security patches. To update, pull the latest images and recreate the containers:

cd /opt/outline
docker compose pull
docker compose up -d

Outline automatically runs any necessary database migrations when the new version starts. Check the release notes before updating to be aware of any breaking changes or new environment variables that may be required.

Monitor disk usage for the PostgreSQL and MinIO volumes, as these will grow over time with documents and file uploads. Set up alerts for disk space and memory usage to avoid service disruptions. Review the Outline logs periodically for errors or warnings:

docker compose logs --since 24h outline | grep -i error

Prefer Managed Hosting?

Outline's multi-service architecture — PostgreSQL, Redis, MinIO, Authentik, Nginx, and the application itself — creates a deployment that is powerful but demands ongoing attention. Database tuning, certificate renewals, security updates across six containers, backup orchestration, and monitoring all require regular maintenance. If your team would rather focus on writing documentation than managing infrastructure, MassiveGRID's fully managed dedicated cloud servers handle the entire operational burden. Their team manages server provisioning, security hardening, updates, backups, and 24/7 monitoring while you get a production-grade Outline deployment that just works. With dedicated resources, Proxmox high-availability clustering, and Ceph triple-replicated storage, your knowledge base stays online and performant without your team ever needing to SSH into a server.