Skip to content

Self-host HedgeDoc

updated Sep 2026prices checked · Jul 2026Install verified on Ubuntu 26.04 · Sep 2026 · how we test
We earn commissions when you shop through the links below. Full disclosure →

A real-time collaborative Markdown editor: several people edit the same note in the browser at once, and notes can be published as read-only pages or presented as slides. Formerly known as CodiMD.

Key facts

CategoryNotes
LicenseAGPL-3.0
StackNode.js, TypeScript, PostgreSQL
Min RAM512 MB
Official imageyes
Measured idle RAM210 MB
Difficulty

What you need

  • Any VPS with at least 512 MB of RAM
  • A domain you control — most self-hosted setups need HTTPS in front of them
  • Under an hour if you've used Docker before
Where to host itaffiliate disclosure
Hetzner Cloudrun it on
From $23.59/mo · 2 vCPU / 4 GB / 80 GB · EU + US
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
From $4/mo · 1 vCPU / 1 GB / 20 GB · US + EU + Asia
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
From $6/mo · 1 vCPU / 1 GB / 25 GB · US + EU + Asia
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install

Run these commands on your server:

# HedgeDoc 1.x + PostgreSQL — upstream's docker-compose example, with generated secrets
mkdir -p ~/hedgedoc && cd ~/hedgedoc
cat > .env <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 24)
CMD_SESSION_SECRET=$(openssl rand -hex 32)
EOF
# postgres 18+ refuses the older /var/lib/postgresql/data mount that upstream's example still shows, so the volume goes on /var/lib/postgresql
cat > docker-compose.yml <<'EOF'
services:
  database:
    image: postgres:18.6-alpine
    environment: { POSTGRES_USER: hedgedoc, POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}", POSTGRES_DB: hedgedoc }
    volumes: ["database:/var/lib/postgresql"]
    restart: always
  app:
    image: quay.io/hedgedoc/hedgedoc:1.12.0
    environment:
      CMD_DB_URL: "postgres://hedgedoc:${POSTGRES_PASSWORD}@database:5432/hedgedoc"
      CMD_SESSION_SECRET: "${CMD_SESSION_SECRET}"
      CMD_DOMAIN: SERVER_IP
      CMD_URL_ADDPORT: "true"
    volumes: ["uploads:/hedgedoc/public/uploads"]
    ports: ["3000:3000"]
    restart: always
    depends_on: [database]
volumes: { database: {}, uploads: {} }
EOF
docker compose up -d  #  →  http://SERVER_IP:3000
# behind a TLS proxy set CMD_DOMAIN to your domain, CMD_PROTOCOL_USESSL=true and drop CMD_URL_ADDPORT

More in Notes

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.