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
:80as 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/hedgedocholding the SQLite database and image uploads hedgedoc.serviceandnginx.serviceas 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 1010Step 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 --versionBoth 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/uploadsThe 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.comAfter 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/hedgedocdata disk, or copydb.hedgedoc.sqliteand theuploadsdirectory.
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
Overview
HedgeDoc is the open-source real-time collaborative Markdown editor, and the community fork of HackMD. This image runs HedgeDoc 1.10.8 on Ubuntu 24.04 LTS.
HedgeDoc is installed from the official release tarball, which ships the pre-built frontend, on the Node.js 20 LTS runtime. An unprivileged hedgedoc system account runs it under a systemd service that starts it on boot and restarts it on failure.
What you get:
- The application listening on the loopback address
127.0.0.1:3000by design, so it is never exposed directly - nginx as a reverse proxy on port 80 with WebSocket upgrade support. Real-time collaboration runs over socket.io websockets, so the proxy passes
UpgradeandConnectionheaders through to the application, and visitors reach HedgeDoc on the standard HTTP port - Secure defaults: anonymous access and open self-registration are disabled
- A one-shot service on the first boot of every deployed instance that generates a fresh session secret, seeds a unique admin account with a random password through HedgeDoc's own user management tooling, and writes the credentials to
/root/hedgedoc-credentials.txt(mode0600, readable only by root). Two instances launched from the same AMI never share credentials, and no default account ships in the image - Persistent data, meaning the SQLite database (
db.hedgedoc.sqlite, holding notes, users and revisions) and uploaded images, under/var/lib/hedgedocon a dedicated, independently resizable EBS data volume kept separate from the operating system disk - A default security group for this listing that opens port 22 (SSH) and port 80 (HTTP) only
Prerequisites
Before you deploy this image you need:
- An AWS account subscribed to this product in AWS Marketplace
- An EC2 key pair in your target region for SSH access
- A security group that allows inbound TCP 22 (SSH) from your IP and TCP 80 (HTTP) from your users
- An instance type of
t3.smallor larger (recommended)
Connecting to your instance
Connect over SSH as the default login user for your operating system variant, using the key pair you launched with.
- Ubuntu 24.04: login user ubuntu, for example ssh -i your-key.pem ubuntu@<instance-public-ip>
Step 1 - Launch from the AWS Marketplace console
- Open the product page in AWS Marketplace and choose Continue to Subscribe, then Continue to Configuration.
- Select the software version and your AWS Region, then choose Continue to Launch.
- Choose an instance type (
t3.smallor larger), your VPC subnet, the security group described above and your EC2 key pair. - Launch the instance, and wait for it to reach the running state with status checks passed.
Step 2 - Launch from the AWS CLI (alternative)
Launch the instance from the CLI. Replace the AMI id, key name, security group and subnet with your own values:
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type t3.small \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--subnet-id subnet-xxxxxxxx \
--metadata-options "HttpTokens=required" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=hedgedoc}]'Step 3 - Retrieve the per-instance admin credentials
SSH to the instance and read the credentials file written on first boot. The file is mode 0600 and owned by root, so read it with sudo:
sudo cat /root/hedgedoc-credentials.txtThe file shows the unique URL and admin credentials generated for this instance:
# HedgeDoc - generated on first boot by hedgedoc-firstboot.service
# These credentials are unique to this VM. Store them somewhere safe. HEDGEDOC_URL=http://203.0.113.10/ HEDGEDOC_ADMIN_EMAIL=admin@hedgedoc.local HEDGEDOC_ADMIN_PASSWORD=<set-at-first-boot>
# Browse to the URL above and choose "Sign In", then sign in with the e-mail
# and password above. Anonymous access and open registration are disabled.
# Create more users: see the user guide (bin/manage_users).
# App: /opt/hedgedoc Config: /etc/hedgedoc/hedgedoc.env
# Data (SQLite DB + uploads): /var/lib/hedgedoc (dedicated EBS volume)Step 4 - Confirm the service is healthy
HedgeDoc runs on loopback 127.0.0.1:3000 and nginx fronts it on port 80. Confirm both services are active:
systemctl is-active hedgedoc.service nginx.serviceactive activeConfirm the listeners. HedgeDoc is loopback-only on 3000, and nginx is on 80:
sudo ss -tlnp | grep -E ':80 |:3000 'LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=3621,fd=20)) LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",...))Because anonymous access is disabled, the unauthenticated session endpoint reports forbidden until you sign in:
curl -s http://127.0.0.1/me{"status":"forbidden"}Verify the generated admin account end to end with a login round trip through the proxy:
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-)
CJ=$(mktemp)
curl -s -o /dev/null -c "$CJ" --data-urlencode "email=$EMAIL" --data-urlencode "password=$PASS" http://127.0.0.1/login
curl -s -b "$CJ" http://127.0.0.1/me rm -f "$CJ"{"status":"ok","id":"35d7b07c-411f-42d0-886f-fd6dc86b4821","name":"admin","photo":"https://cdn.libravatar.org/avatar/1a43fa92ea14849c2273769e6c21db66?default=identicon&s=96"}Step 5 - Sign in
Browse to http://<instance-public-ip>/ and choose Sign In. Under Sign in via E-Mail, enter the e-mail address and password from Step 3.
Step 6 - Write and collaborate in real time
After you sign in, choose New note. The split-pane editor shows your Markdown on the left and the rendered result on the right, live as you type.
Share the note URL with other signed-in users and edit together in real time. Everyone's cursors and changes appear instantly over the WebSocket connection.
Notes support GitHub-flavoured Markdown plus code blocks with syntax highlighting, task lists, tables, MathJax math, sequence and flow diagrams, and image uploads (stored on the data volume). Use the view-mode buttons in the toolbar to switch between editor, split and preview modes, and choose Publish to share a read-only rendered view. You can also present any note as a slide deck from Menu, then Slide Mode.
Step 7 - Manage users
Open self-registration is disabled, so create accounts from the command line with HedgeDoc's bundled manage_users tool. Create a user, and remove one, like this:
cd /opt/hedgedoc
sudo -u hedgedoc env HOME=/var/lib/hedgedoc NODE_ENV=production CMD_DB_URL=sqlite:///var/lib/hedgedoc/db.hedgedoc.sqlite node bin/manage_users --add demo@example.com --pass 'ChangeMe-Demo-12345'
sudo -u hedgedoc env HOME=/var/lib/hedgedoc NODE_ENV=production CMD_DB_URL=sqlite:///var/lib/hedgedoc/db.hedgedoc.sqlite node bin/manage_users --del demo@example.comUsing password from commandline... Created user with email demo@example.com Deleted user demo@example.com ...Use --reset instead of --add to change an existing user's password.
To allow open self-registration instead, set CMD_ALLOW_EMAIL_REGISTER=true in /etc/hedgedoc/hedgedoc.env and restart the service. Alternatively, connect an external identity provider (LDAP, SAML, OAuth2, GitHub, GitLab and more) with the corresponding CMD_* variables. See the HedgeDoc configuration documentation at docs.hedgedoc.org.
Step 8 - Enable HTTPS (recommended)
For production use, terminate TLS so that traffic, including the WebSocket collaboration channel, is encrypted. There are two common options.
Option A: certbot on the instance. Point a DNS name at the instance, open port 443 in the security group, then install certbot and obtain a certificate. Run these commands interactively, because they prompt for your e-mail and domain:
- Install the packages:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx - Obtain and install the certificate:
sudo certbot --nginx -d your-domain.example.com
After you enable TLS, tell HedgeDoc that its public URL is HTTPS. Set CMD_DOMAIN=your-domain.example.com and CMD_PROTOCOL_USESSL=true in /etc/hedgedoc/hedgedoc.env, then run sudo systemctl restart hedgedoc.service.
Option B: terminate TLS upstream. Place an AWS Application Load Balancer or Amazon CloudFront in front of the instance with an ACM certificate, forwarding to port 80. Make sure WebSocket support is enabled on the load balancer. Set CMD_PROTOCOL_USESSL=true and CMD_DOMAIN as above so that generated links use HTTPS.
Step 9 - Configuration and data locations
These paths matter for configuration and data:
- /etc/hedgedoc/hedgedoc.env: server configuration (CMD_* variables) covering domain, ports, database URL, auth settings and session secret
- /opt/hedgedoc: the HedgeDoc application (release tarball and production dependencies)
- /var/lib/hedgedoc: data volume mount holding db.hedgedoc.sqlite (notes, users, revisions) and uploads/ (images)
- /root/hedgedoc-credentials.txt: the per-instance admin credentials generated on first boot (mode 0600)
- /etc/nginx/sites-available/sac-hedgedoc: the nginx reverse-proxy site configuration (WebSocket upgrade headers)
Confirm that the data tier is on its own volume:
df -h /var/lib/hedgedoc | tail -1/dev/nvme1n1 20G 52M 19G 1% /var/lib/hedgedocStep 10 - Service management
Check the HedgeDoc service status:
systemctl status hedgedoc.service --no-pagerRestart HedgeDoc or nginx after a configuration change:
- Restart HedgeDoc with
sudo systemctl restart hedgedoc.service - Restart nginx with
sudo systemctl restart nginx.service
Step 11 - Backup and maintenance
- Back up the data volume. Take regular EBS snapshots of the
/var/lib/hedgedocvolume, or copydb.hedgedoc.sqliteand theuploads/directory to off-instance storage. This captures every note, revision, user and image. - Keep the OS patched. Apply Ubuntu security updates regularly with
sudo apt-get update && sudo apt-get upgrade. - Resize storage. Because the data lives on a dedicated EBS volume, you can grow it with the AWS console or CLI and then extend the filesystem, with no change to the OS disk.
- Upgrades. HedgeDoc 1.10.x is the stable line. Before any upgrade, snapshot the data volume. Then unpack the new release tarball over
/opt/hedgedocand re-run its setup per the HedgeDoc release notes.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and chat. Support covers HedgeDoc deployment, user management, authentication providers, TLS termination, backups, upgrades and scaling.
All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Launch HedgeDoc on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com