Self-Hosting Outline: Your Team Wiki 2026
Outline is the best-looking open source wiki and knowledge base. It's a Notion and Confluence alternative with real-time collaboration, Markdown support, and a clean interface your team will actually enjoy using.
Requirements
- VPS with 2 GB RAM minimum
- Docker and Docker Compose
- Domain name (e.g.,
wiki.yourdomain.com) - S3-compatible storage (MinIO self-hosted or AWS S3)
- OIDC authentication provider (required — Outline has no built-in auth)
- SMTP service
Important: Authentication Requirement
Outline requires an external authentication provider. No username/password login. Options:
| Provider | Difficulty | Notes |
|---|---|---|
| Google Workspace | Easy | If your team uses Google |
| GitHub | Easy | Good for dev teams |
| Keycloak (self-hosted) | Medium | Full control, any team |
| Authentik (self-hosted) | Medium | Modern alternative to Keycloak |
| Azure AD | Easy | If your team uses Microsoft |
Step 1: Set Up MinIO (S3 Storage)
Outline requires S3-compatible storage for file uploads:
# Add to docker-compose.yml
services:
minio:
image: minio/minio:latest
container_name: outline-minio
restart: unless-stopped
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
environment:
- MINIO_ROOT_USER=outline
- MINIO_ROOT_PASSWORD=your-minio-password
command: server /data --console-address ":9001"
After starting MinIO:
- Open
http://your-server:9001 - Create a bucket named
outline - Set bucket policy to allow the Outline service account
Step 2: Create Docker Compose
# docker-compose.yml
services:
outline:
image: outlinewiki/outline:latest
container_name: outline
restart: unless-stopped
ports:
- "3000:3000"
env_file: .env
depends_on:
- postgres
- redis
- minio
postgres:
image: postgres:16-alpine
container_name: outline-db
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=outline
- POSTGRES_USER=outline
- POSTGRES_PASSWORD=your-strong-password
redis:
image: redis:7-alpine
container_name: outline-redis
restart: unless-stopped
volumes:
- redis_data:/data
minio:
image: minio/minio:latest
container_name: outline-minio
restart: unless-stopped
volumes:
- minio_data:/data
environment:
- MINIO_ROOT_USER=outline
- MINIO_ROOT_PASSWORD=your-minio-password
command: server /data --console-address ":9001"
volumes:
postgres_data:
redis_data:
minio_data:
Step 3: Configure Environment
Create .env:
# General
NODE_ENV=production
SECRET_KEY=your-random-secret-key-min-32-chars
UTILS_SECRET=your-random-utils-secret-min-32-chars
URL=https://wiki.yourdomain.com
PORT=3000
# Database
DATABASE_URL=postgres://outline:your-strong-password@postgres:5432/outline
DATABASE_CONNECTION_POOL_MIN=0
DATABASE_CONNECTION_POOL_MAX=10
PGSSLMODE=disable
# Redis
REDIS_URL=redis://redis:6379
# Storage (MinIO)
FILE_STORAGE=s3
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
AWS_ACCESS_KEY_ID=outline
AWS_SECRET_ACCESS_KEY=your-minio-password
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
# SMTP
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USERNAME=resend
SMTP_PASSWORD=re_your_api_key
SMTP_FROM_EMAIL=wiki@yourdomain.com
SMTP_REPLY_EMAIL=wiki@yourdomain.com
SMTP_SECURE=true
# Authentication (Google example)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Or OIDC (Keycloak/Authentik)
# OIDC_CLIENT_ID=outline
# OIDC_CLIENT_SECRET=your-oidc-secret
# OIDC_AUTH_URI=https://auth.yourdomain.com/realms/master/protocol/openid-connect/auth
# OIDC_TOKEN_URI=https://auth.yourdomain.com/realms/master/protocol/openid-connect/token
# OIDC_USERINFO_URI=https://auth.yourdomain.com/realms/master/protocol/openid-connect/userinfo
# OIDC_LOGOUT_URI=https://auth.yourdomain.com/realms/master/protocol/openid-connect/logout
# OIDC_DISPLAY_NAME=SSO Login
Generate secrets:
openssl rand -hex 32 # SECRET_KEY
openssl rand -hex 32 # UTILS_SECRET
Step 4: Start Outline
docker compose up -d
# Run database migrations
docker exec outline node build/server/scripts/seed.js
Step 5: Reverse Proxy (Caddy)
# /etc/caddy/Caddyfile
wiki.yourdomain.com {
reverse_proxy localhost:3000
}
sudo systemctl restart caddy
Step 6: Organize Your Wiki
Recommended collection structure:
| Collection | Purpose |
|---|---|
| 📋 Company | Mission, values, org chart, policies |
| 🚀 Engineering | Architecture, runbooks, onboarding |
| 📊 Product | Specs, roadmap, user research |
| 💼 Sales | Playbooks, pricing, case studies |
| 👋 Onboarding | New hire guides, setup instructions |
| 📝 Meeting Notes | Standups, retros, all-hands |
Tips:
- Use templates for recurring documents (meeting notes, RFCs, postmortems)
- Star important documents for quick access
- Use
/commands in the editor for blocks, embeds, tables - Drag and drop to reorder documents and collections
Step 7: Integrations
| Integration | Setup |
|---|---|
| Slack | Settings → Integrations → Slack (notifications + search) |
| Zapier/n8n | Use Outline's API with webhooks |
| Import from Notion | Settings → Import → Notion (native importer) |
| Import from Confluence | Settings → Import → Confluence |
Production Hardening
Backups:
# Database backup (daily cron)
docker exec outline-db pg_dump -U outline outline > /backups/outline-$(date +%Y%m%d).sql
# MinIO data backup
docker run --rm -v minio_data:/data -v /backups:/backup alpine \
tar czf /backup/outline-files-$(date +%Y%m%d).tar.gz /data
Updates:
docker compose pull
docker compose up -d
Performance:
- Increase
DATABASE_CONNECTION_POOL_MAXfor larger teams - Add PostgreSQL connection pooling (PgBouncer) for 100+ users
- Consider CDN for MinIO assets
Resource Usage
| Users | RAM | CPU | Disk |
|---|---|---|---|
| 1-20 | 2 GB | 2 cores | 10 GB |
| 20-50 | 4 GB | 2 cores | 20 GB |
| 50-200 | 8 GB | 4 cores | 50 GB |
VPS Recommendations
| Provider | Spec (30 users) | Price |
|---|---|---|
| Hetzner | 4 vCPU, 8 GB RAM | €8/month |
| DigitalOcean | 2 vCPU, 4 GB RAM | $24/month |
| Linode | 2 vCPU, 4 GB RAM | $24/month |
Why Self-Host Outline
Outline Cloud's pricing scales with seats: the Team plan is $10/user/month, which means a 15-person company pays $150/month or $1,800/year. Notion charges $15/user/month for the Business plan. At 15 users, that's $2,700/year. Confluence Cloud is similarly priced at $8.15/user/month, and the costs compound as teams grow. Self-hosted Outline, by contrast, costs only your server bill — typically $8–20/month on a Hetzner or DigitalOcean instance regardless of how many users you add.
The math is stark for growing teams. A 30-person company self-hosting Outline pays approximately $100/year in infrastructure versus $3,600/year on Outline Cloud. Within the first year, the investment in setting up self-hosting pays back many times over.
Data ownership and compliance: Confluence and Notion route your company's internal knowledge — architecture docs, HR policies, customer data — through third-party infrastructure. For companies in regulated industries or jurisdictions with strict data residency requirements, this creates compliance risk. Self-hosting Outline means your wiki data never touches Atlassian's or Notion's servers. Legal, healthcare, and financial services teams often require this.
Customization: Self-hosted Outline lets you configure storage backends, authentication providers, and retention policies in ways the cloud product doesn't allow. You can pair it with self-hosted Keycloak for SSO, MinIO for on-premises file storage, and your own SMTP server for complete infrastructure independence.
When NOT to self-host Outline: If your team has fewer than 5 people, the free tiers on competing products may serve you well enough. Outline's setup is more involved than most self-hosted tools — it requires MinIO (or AWS S3) for file storage and an external OIDC provider for authentication; there is no simple username/password login. If you don't have someone comfortable managing Docker services, consider whether the operational overhead is worth it. For very small teams with no DevOps capacity, Notion's free tier or GitHub Wikis may be the pragmatic choice.
Prerequisites (Expanded)
Before deploying Outline in a production environment, it helps to understand why each requirement matters rather than treating the list as a checklist to rush through.
VPS with 2 GB RAM minimum: Outline is a Node.js application that loads a sizable JavaScript bundle on startup. With only 1 GB RAM, the process risks OOM-killing under moderate load, especially during database migrations or after a fresh deployment. For teams larger than 10 people, start with 4 GB. Ubuntu 22.04 LTS is the recommended OS — it has long-term support through 2027, wide community knowledge, and predictable package versions. Avoid running on cutting-edge distros in production; stability matters more than novelty.
Docker and Docker Compose: Outline's setup expects Docker Compose v2+ (the docker compose subcommand, not the legacy docker-compose). Verify with docker compose version before starting. The Compose file orchestrates four containers — Outline, PostgreSQL, Redis, and MinIO — as a single unit.
S3-compatible storage: Outline stores all file attachments (images, PDFs, documents) in an S3-compatible bucket. This is not optional. The most common self-hosted choice is MinIO, which we've included in the Compose file above. Alternatively, you can use a real AWS S3 bucket, Cloudflare R2, or Backblaze B2 — all are S3-compatible.
OIDC provider: This is the most common stumbling block. Outline has no built-in password login; it delegates authentication entirely to an external provider. If your team uses Google Workspace, GitHub, or Microsoft 365, this is straightforward. If you want a fully self-hosted solution, pair Outline with self-hosted Keycloak or Authentik.
When choosing a VPS, providers like Hetzner (Europe) and Vultr offer good value for small teams. See our VPS comparison for self-hosters for a full breakdown of providers, pricing, and network performance that will help you make the right infrastructure choice.
Production Security Hardening
Running Outline in production means you're protecting your company's internal knowledge base. Weak security here has real consequences — a compromised wiki exposes architecture docs, credentials that got accidentally pasted, and sensitive business information.
Firewall with UFW: Lock down your server to only the ports that need to be publicly accessible.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP (for Caddy's ACME challenge)
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
Never expose PostgreSQL (5432), Redis (6379), or MinIO (9000/9001) to the public internet. These ports should only be reachable within Docker's internal network.
Fail2ban for SSH protection: Brute-force attacks on SSH are constant background noise on any public server.
sudo apt install fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit /etc/fail2ban/jail.local to set maxretry = 3 and bantime = 3600 under [sshd]. Restart with sudo systemctl restart fail2ban.
Keep secrets out of docker-compose.yml: Never hardcode passwords or secret keys directly in the Compose file. The .env file approach used in this guide is correct — but make sure .env is never committed to version control. Add it to .gitignore. For additional security, consider using Docker secrets or a secrets manager for production deployments.
Disable SSH password authentication: Once you have SSH key-based access working, disable password auth entirely:
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
Automatic security updates: Enable unattended upgrades to patch the OS automatically:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
This handles kernel patches, OpenSSL updates, and other security fixes without manual intervention. Limit it to security updates only to avoid accidental application breakage.
Disable Outline signups after setup: Once your team accounts are created, set WIKI_RESTRICTED_DOMAIN in your .env file so only email addresses from your domain can register — or use OIDC authentication scoped to your identity provider, which handles this automatically.
For a complete server hardening checklist beyond what's covered here, see our self-hosting security checklist, which covers certificate pinning, log monitoring, and intrusion detection for self-hosted services.
Troubleshooting Common Issues
Outline container exits immediately on startup
This almost always means a missing or incorrectly formatted .env variable. Outline is strict about its required configuration.
docker compose logs outline
Look for lines like Error: Missing required environment variable: SECRET_KEY. The most common culprits are a SECRET_KEY that's too short (must be 32+ chars), an invalid DATABASE_URL format, or a URL that doesn't include the protocol (https://).
Files and images won't upload
Outline will log Error: connect ECONNREFUSED minio:9000 if MinIO isn't running or reachable. Check:
docker compose ps
docker compose logs minio
If MinIO is running, verify the bucket exists and the bucket policy allows the Outline service account to write. Log into the MinIO console at http://your-server:9001 and confirm the outline bucket is present and the access policy is set to allow the credentials in your .env.
OIDC authentication fails after clicking "Sign in with Google"
If you're redirected back to the login page with no error, check that your OAuth callback URL exactly matches what you registered in your identity provider. For Google, the authorized redirect URI must be https://wiki.yourdomain.com/auth/oidc.callback — the trailing path matters. Also verify that GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env match your Google Cloud Console credentials.
Real-time collaboration isn't working (changes don't sync)
Outline uses WebSockets for real-time collaboration. If edits don't appear live across multiple browser tabs, your reverse proxy may be stripping WebSocket upgrade headers. With Caddy, this is handled automatically. If you're using Nginx, add the following to your proxy configuration:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Database migration fails on first run
If docker exec outline node build/server/scripts/seed.js exits with a PostgreSQL connection error, the database container may still be initializing. Wait 10 seconds and try again. You can watch the DB startup with docker compose logs postgres. The database is ready when you see database system is ready to accept connections.
MinIO console is accessible from the internet
By default, MinIO's admin console runs on port 9001. If you've opened this port in your firewall, it's publicly accessible with only the MinIO root credentials standing between attackers and your files. Close port 9001 in UFW immediately. Access the MinIO console only by SSH tunneling: ssh -L 9001:localhost:9001 user@your-server.
For Outline deployed at team scale, monitoring the application health proactively is important. The application exposes a /api/health endpoint — add it to your Uptime Kuma instance to get alerted if the Outline service becomes unavailable. Additionally, monitor your PostgreSQL instance for slow queries as the wiki grows: Outline's full-text search relies on PostgreSQL's built-in text search capabilities, and large wikis (over 5,000 documents) can develop slow query issues if the database isn't properly vacuumed and indexed. Schedule a weekly VACUUM ANALYZE job to keep PostgreSQL's statistics current and queries fast. These maintenance steps take under an hour to set up and prevent the most common performance degradation issues that large Outline deployments encounter.
See the VPS comparison for self-hosters to choose the right server for your Outline deployment.
Set up automated server backups with restic to protect your Outline database and MinIO file storage.
Review the self-hosting security checklist before going to production.
Compare wiki and knowledge base tools on OSSAlt — features, import support, and pricing side by side.
See open source alternatives to Outline on OSSAlt.