shouldiselfhost?they tell you if you can — we tell you if you should
THE DECISION LAYER FOR SELF-HOSTING100 APPS UNDER COVERAGEPROTOCOL v1NO VENDOR HAS EVER PAID FOR A RATING
§
NOTES-WIKIS
Notion logo

Should I self-host Notion?

$12/mo · Plus per seat · alternative: Outline
RATING
KINDA
OUTLOOK
NOT ASSIGNED
RATIONALE

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.

WHAT YOU LOSE
01

Databases: views, relations, rollups — Outline has none of it, and that's half of many workspaces

02

Native mobile apps (Outline's mobile web is fine, not great)

03

Notion AI and the template ecosystem

04

Sign-in needs an auth provider you run or configure (magic-link SMTP or OIDC)

WHAT YOU'RE PAYING FOR

docs & wiki with nested pages · full-text search · real-time collaboration · databases with views, relations and rollups · mobile apps · public sharing links

Self-Hosting Facts
1 app per panel · Notion
Assumes 1 seat on 1 always-on box you already run
Amount per month% of sub *
Subscription$12.00
VPS share1.5013%
Storage + backup0.202%
Your hours · 30 min @ $2010.0083%
Self-hosted all-in11.7098%
Effective markup1.0×
Setup · 75 min measured$25.00 once
Break-even83.3 months
VERDICT
KINDA
OUTLOOK
NOT ASSIGNED

* Percent of the subscription price. Your time is priced at the slider's rate — $0/h ("my homelab time is free and I love it") is a legitimate position. Every number is derived from the record, never hand-written. Price source, checked 2026-08-05.

Runs comfortably on the old laptop in your closet.

01
ALTERNATIVES
Ranked alternatives
01Outline logo
OutlineA fast, collaborative wiki that covers the half of Notion most teams actually live in.
outline/outline · ★ 40,072 · 0d
02Docmost logo
DocmostSpaces, nested pages and an editor two people can type in at once, with every word in a Postgres you can dump.
docmost/docmost · ★ 21,316 · 0d
Outline running from the boot-verified compose file
Outline, running from the compose file on this page — screenshot from our verified instance, not marketing material.
02
EVIDENCE
PROTOCOL
v1
VERIFIED BY
zernonia
REVIEWED
10 AUG 2026
SETUP MEASURED
75 MIN
AI ASSISTANT
CLAUDE-CODE · STATED
WHAT BROKE
01

maildev (the first mail catcher we tried) crash-loops wherever IPv6 is disabled — it insists on binding '::'; swapped to mailpit

02

Outline boots healthy without SMTP but magic-link sign-in silently goes nowhere — the compose ships mailpit so the workflow is completable locally

THE TESTED COMPOSE FILE — CI-BOOTED WEEKLY
# 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.

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.
03
HISTORY
10 AUG 2026UNRATED → KINDAInitial verdict: docs half moves in an evening, databases and mobile don't move at all.