
Should I self-host Notion?
The docs half moves in an evening and the math is per-seat — but Notion's databases and mobile apps don't move at all. Worth it if you write more than you rollup.
The math (nothing hidden, including your time)
| Notion (Plus, per seat) | $12.00/mo |
| VPS share + storage | −$1.70/mo |
| Your maintenance: 30 min/mo at $20/h | −$10.00/mo |
| Net saving | $0.30/mo |
| Setup: 75 min measured (one-time) | $25.00 |
| Break-even | 83.3 months |
| Markup Index (price ÷ real self-host cost) | 1.0× |
Price: source, checked 2026-08-05. Inputs are stored in git; every number above is derived, never hand-written.
What you lose
- Databases: views, relations, rollups — Outline has none of it, and that's half of many workspaces
- Native mobile apps (Outline's mobile web is fine, not great)
- Notion AI and the template ecosystem
- Sign-in needs an auth provider you run or configure (magic-link SMTP or OIDC)
What you're paying Notion for
- docs & wiki with nested pages
- full-text search
- real-time collaboration
- databases with views, relations and rollups
- mobile apps
- public sharing links

Ranked alternatives
A fast, collaborative wiki that covers the half of Notion most teams actually live in.
Spaces, nested pages and an editor two people can type in at once, with every word in a Postgres you can dump.
Hardware
Runs comfortably on the old laptop in your closet.
The tested compose file (our evidence, CI-booted)
# Outline — the docs/wiki half of Notion. THE worked example (see logs/notion-outline.md).
# Sign-in is magic-link email: mailpit catches the mail locally so the core workflow is
# completable without any external SMTP or OIDC provider. In production point SMTP_* at a real
# mailbox (or configure OIDC) and put the whole thing behind TLS.
# Secrets below are throwaway test values — generate your own: openssl rand -hex 32
services:
outline:
image: outlinewiki/outline:latest
container_name: outline
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
NODE_ENV: production
URL: http://localhost:3000
PORT: "3000"
SECRET_KEY: 5f31f5ad9e6f6ff0c1b52c11a03f5f4b7b1b4a4f3f2ce9d6be2c8f4d7a91d2e3
UTILS_SECRET: 8d2b1f9c4a7e6d5b3c2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c
DATABASE_URL: postgres://outline:outline@postgres:5432/outline
PGSSLMODE: disable
REDIS_URL: redis://redis:6379
FILE_STORAGE: local
FILE_STORAGE_LOCAL_ROOT_DIR: /var/lib/outline/data
SMTP_HOST: mailpit
SMTP_PORT: "1025"
SMTP_FROM_EMAIL: outline@localhost.test
SMTP_SECURE: "false"
ports:
- "3000:3000"
volumes:
- outline-data:/var/lib/outline/data
healthcheck:
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3000/_health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
interval: 5s
timeout: 5s
retries: 36
start_period: 30s
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: outline
POSTGRES_DB: outline
volumes:
- outline-pg:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 5s
timeout: 5s
retries: 12
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 5s
timeout: 5s
retries: 12
mailpit:
# (maildev crashes where IPv6 is disabled — it insists on binding "::". Mailpit doesn't.)
image: axllent/mailpit:latest
restart: unless-stopped
ports:
- "1080:8025" # web UI to read the magic-link mail
healthcheck:
test: ["CMD", "/mailpit", "readyz"]
interval: 5s
timeout: 5s
retries: 12
volumes:
outline-data:
outline-pg:
Want a guided install instead? caniselfhostit.com/notion has AI-agent prompts that assume a bare machine — that's their half of the stool, and it's good.
Verification
Protocol v1 · verified by zernonia on 2026-08-10 · setup measured at 75 min · AI assistant: claude-code (allowed & stated)
What broke
- maildev (the first mail catcher we tried) crash-loops wherever IPv6 is disabled — it insists on binding '::'; swapped to mailpit
- Outline boots healthy without SMTP but magic-link sign-in silently goes nowhere — the compose ships mailpit so the workflow is completable locally
Full timed log
# Timed setup log: Notion → Outline **Protocol:** v1 · **Verified by:** zernonia · **Date:** 2026-08-10 **Assistant:** claude-code (allowed & stated — protocol v1 assumes an AI sysadmin) **Environment:** containerized runner, 2 vCPU class, Debian-based, Docker 29.3 / Compose v5.1 preinstalled > This is THE worked example — the template every contributor copies. Format: environment, > timeline, measurements, what broke. Honest numbers or nothing. ## Timeline (wall clock) | Step | Time | |---|---| | Read Outline's docker docs, draft compose (postgres + redis + outline + mail catcher) | 22 min | | Generate secrets (`openssl rand -hex 32` × 2), first `docker compose up` | 4 min | | **Boot #1 failed** — mail catcher crash-looping (see What broke) | 12 min to diagnose | | Swap maildev → mailpit, re-up | 3 min | | **Boot #2: all 4 services healthy in 44 s (measured, `compose up --wait`)** | 1 min | | Core workflow: magic-link sign-in via mailpit UI, create doc, edit, search finds it | 18 min | | Cleanup, re-run from scratch to confirm reproducibility | 15 min | | **Total: ~75 min** | | ## Measurements - `docker compose up --wait` → healthy: **44 s** (clean volumes, images cached) - Containers: 4 (outline, postgres:16, redis:7, mailpit) - Idle RAM after sign-in: outline ~480 MB + postgres ~40 MB + redis ~5 MB — 2 GB box is comfortable, 1 GB is not with anything else running ## What broke 1. **maildev crashes where IPv6 is disabled** (`listen EAFNOSUPPORT: address family not supported :::1025`). Many CI runners and some VPS images disable IPv6; maildev insists on binding `::`. Swapped to **mailpit**, which binds IPv4 fine and has a `readyz` probe. 2. **Outline without SMTP is a locked door**: it boots healthy but magic-link sign-in silently goes nowhere until SMTP_* points somewhere real. The compose ships mailpit so the workflow is completable locally; in production use a real mailbox or OIDC. ## Verdict-relevant notes - The docs/wiki/search/collab jobs all work. There is no databases feature — Notion's views/relations/rollups have no equivalent here. That gap, not the setup, drives the KINDA. - Notion import exists (Markdown/HTML export → import) but attachments need re-linking.
Verdict history
- 2026-08-10: unscored → KINDA — Initial verdict: docs half moves in an evening, databases and mobile don't move at all.
