Deployment guide

HedgeDoc

Overview

HedgeDoc is the leading open-source, self-hosted, real-time collaborative Markdown editor, and the community fork of HackMD. Several people edit the same note at once in a split-pane editor with a live rendered preview and presence cursors, all in the browser. Notes support diagrams, code blocks with syntax highlighting, MathJax math, a slide-deck presentation mode and image uploads.

This image installs HedgeDoc 1.10.8 (AGPL-3.0 licensed) to /opt/hedgedoc and runs it on Node.js 20 LTS as a hardened systemd service bound to the loopback address. nginx fronts it on port 80, with the WebSocket upgrade support that real-time collaboration needs. The SQLite database and image uploads live on a dedicated Azure data disk.

Anonymous note creation and open self-registration are disabled. A unique admin account and session secret are generated on the first boot of every VM and written to a root-only file, so no shared or default credentials ship in the image.

What you get:

  • HedgeDoc 1.10.8 in /opt/hedgedoc, running on Node.js 20 LTS
  • The HedgeDoc application bound to loopback (127.0.0.1:3000), never exposed directly
  • nginx on :80 as a reverse proxy to the loopback backend, carrying the socket.io WebSocket upgrade
  • A unique admin account and a fresh session secret generated on first boot, written to a root-only file
  • Anonymous access and open registration disabled by default
  • A dedicated Azure data disk at /var/lib/hedgedoc holding the SQLite database and image uploads
  • hedgedoc.service and nginx.service as systemd units
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size for a small team.

In the network security group (NSG), allow inbound 22/tcp from your management network and 80/tcp.

HedgeDoc serves plain HTTP on port 80. For production, terminate TLS in front of it with your own domain (see Step 13).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for HedgeDoc by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

Review the dedicated data disk on the Disks tab, then select Review + create and Create.

Step 2 - Deploy from the Azure CLI

Accept the Marketplace terms, create the VM and open port 80:

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:hedgedoc-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name hedgedoc \
  --image sac1647283583153:hedgedoc-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name hedgedoc --port 80 --priority 1010

Step 3 - Connect to your VM

Connect over SSH as azureuser:

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services and data disk

HedgeDoc runs as hedgedoc.service (the Node.js app, bound to loopback), with nginx.service as the public reverse proxy on port 80. The SQLite database and uploads live on a dedicated Azure data disk mounted at /var/lib/hedgedoc. Check the services, listeners, mount and Node.js version:

systemctl is-active hedgedoc.service nginx.service
sudo ss -tlnp | grep -E ':3000|:80 ' findmnt -no SOURCE,TARGET,FSTYPE /var/lib/hedgedoc node --version

Both services report active, the data disk is mounted ext4, and Node.js reports v20.x.

In the listeners, HedgeDoc (3000) is bound to 127.0.0.1 only. The sole public listener is nginx on :80, which proxies to HedgeDoc and carries the WebSocket connections used for real-time collaboration.

Step 5 - Retrieve your admin credentials

The administrator account is generated uniquely on the first boot of your VM and written to a root-only file. Read the file, and confirm that the sign-in page is also served through nginx on port 80:

sudo cat /root/hedgedoc-credentials.txt curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost/

The file contains HEDGEDOC_ADMIN_EMAIL, HEDGEDOC_ADMIN_PASSWORD (your unique password) and HEDGEDOC_URL. The sign-in page returns HTTP 200 through nginx. Store the password somewhere safe.

Step 6 - The security model

HedgeDoc is secure by default. Anonymous access (CMD_ALLOW_ANONYMOUS) and open registration (CMD_ALLOW_EMAIL_REGISTER) are disabled, so only the seeded per-instance admin can sign in and create notes.

The check below shows an unauthenticated request being forbidden, a wrong password being rejected, and only the real per-VM admin obtaining a session:

EMAIL=$(sudo grep '^HEDGEDOC_ADMIN_EMAIL=' /root/hedgedoc-credentials.txt | cut -d= -f2-)
PASS=$(sudo grep '^HEDGEDOC_ADMIN_PASSWORD=' /root/hedgedoc-credentials.txt | cut -d= -f2-)
echo -n 'anonymous /me : '; curl -s http://localhost/me; echo
BAD=$(mktemp); curl -s -o /dev/null -c "$BAD" --data-urlencode "email=$EMAIL" --data-urlencode 'password=wrong-password' http://localhost/login echo -n 'wrong-password /me : '; curl -s -b "$BAD" http://localhost/me; echo; rm -f "$BAD"
GOOD=$(mktemp); curl -s -o /dev/null -c "$GOOD" --data-urlencode "email=$EMAIL" --data-urlencode "password=$PASS" http://localhost/login echo -n 'signed-in /me : '; curl -s -b "$GOOD" http://localhost/me; echo; rm -f "$GOOD"

The anonymous and wrong-password requests both return {"status":"forbidden"}. Only the correct per-VM password returns {"status":"ok"} with the admin identity.

Step 7 - Sign in to the web UI

Browse to http://<vm-public-ip>/ and select Sign In. Sign in via E-Mail with the HEDGEDOC_ADMIN_EMAIL and HEDGEDOC_ADMIN_PASSWORD from Step 5.

Step 8 - Collaborate in real time

Select New to create a note. HedgeDoc opens the split-pane editor, with your Markdown on the left and a live rendered preview on the right. Notes support headings, lists, tables, code blocks with syntax highlighting, math and more.

Share the note link, and colleagues can edit the same note with you at the same time. The count in the top right shows how many people are online.

Step 9 - Your note overview

The History view lists the notes you have opened, with titles, tags and last-visited times, so you can search for and return to any note.

Use New note to start another, or pin the notes you use most.

Step 10 - Present a note as slides

Any note can be presented as a slide deck. Add a type: slide header to the note, separate slides with ---, then open the note's slide view from the Menu.

HedgeDoc renders the note as a reveal.js presentation that you can step through and share.

Step 11 - Where your data lives

All persistent state lives on the dedicated Azure data disk mounted at /var/lib/hedgedoc. That includes the SQLite database and every uploaded image. The disk is kept separate from the OS disk, so you can snapshot and resize it on its own. The image uploads directory is a symlink from the application into the data disk:

findmnt -no SOURCE,TARGET,FSTYPE,SIZE /var/lib/hedgedoc
sudo ls -lh /var/lib/hedgedoc/db.hedgedoc.sqlite ls -l /opt/hedgedoc/public/uploads

The database file db.hedgedoc.sqlite and the uploads directory both sit on the data disk, and /opt/hedgedoc/public/uploads is a symlink onto it.

To back up HedgeDoc, snapshot the /var/lib/hedgedoc data disk, or copy the SQLite database and the uploads directory while the service is quiescent.

Step 12 - Add more users

Open registration is disabled, so add users from the command line with the bundled manage_users tool. Run it as the hedgedoc service user, and load the environment file so that it uses the same database:

cd /opt/hedgedoc
sudo -u hedgedoc env $(sudo grep -v '^#' /etc/hedgedoc/hedgedoc.env | xargs) NODE_ENV=production \
  node bin/manage_users --add teammate@example.com --pass 'a-strong-password'

For central identity, you can also connect LDAP, SAML or OAuth2 providers by adding the relevant CMD_* variables to /etc/hedgedoc/hedgedoc.env and restarting hedgedoc.service.

Step 13 - Enable HTTPS for production

HedgeDoc is served as plain HTTP on port 80. For production, put your own domain in front and terminate TLS. You can install certbot and the nginx plugin, or place the VM behind an Azure Application Gateway or Front Door with a managed certificate:

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d hedgedoc.example.com

After enabling HTTPS, set CMD_DOMAIN to your domain and CMD_PROTOCOL_USESSL=true in /etc/hedgedoc/hedgedoc.env, then restart hedgedoc.service so generated links and the session cookie use https://.

Maintenance

  • Restart the app. Run sudo systemctl restart hedgedoc.service.
  • View logs. Run sudo journalctl -u hedgedoc.service -e.
  • Configuration. Runtime settings live in /etc/hedgedoc/hedgedoc.env, and the app is in /opt/hedgedoc.
  • OS updates. unattended-upgrades is enabled. Apply the latest security updates with sudo apt-get update && sudo apt-get -y upgrade, and reboot if a new kernel is installed.
  • Back up. Snapshot the /var/lib/hedgedoc data disk, or copy db.hedgedoc.sqlite and the uploads directory.

Support

SecureAnyCloud maintains this image and provides 24/7 support. HedgeDoc is licensed under AGPL-3.0.

"HedgeDoc" and "HackMD" are trademarks of their respective owners. SecureAnyCloud provides this image, which is not endorsed by or affiliated with the HedgeDoc project.

Deploy on Azure

Launch HedgeDoc on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure