We have dealt with markdown on several occasions before. We consider markdown to be an essential resource.

HedgeDoc is an open source, web-based software resource that consists of a collaborative markdown editor.

With HedgeDoc, you can write documents in markdown using the web interface through your browser and share them with others, either read-only or by authorizing editing, depending on the permissions you wish to set.

You can use HedgeDoc in self-hosted mode by installing it on your server.

The source code is available on GitHub.

HedgeDoc derives from the fork of CodiMD, as is explained on its history page.

The HedgeDoc project is overseen by the Team Core Developers.


For installing HedgeDoc, you can refer to the instructions on the official page.

HedgeDoc can be installed from Docker image or manually.

Manual installation

We opted for the manual installation, which is probably less simple than installing by docker image.

Prerequisites are required on the server.

We followed the following steps to install HedgeDoc on a server running Ubuntu 20.04.

System upgrade.

  1. update the system with
apt update && apt upgrade -y

User and group creation.

  1. Create the user hedgedoc and the group hedgedoc with
adduser hedgedoc
  • The system will respond as follows:
Adding user `hedgedoc' ...
Adding new group `hedgedoc' (1001) ...
Adding new user `hedgedoc' (1001) with group `hedgedoc' ...
Creating home directory `/home/hedgedoc' ...
Copying files from `/etc/skel' ...
New password:
  • At this point, you can proceed with enter to give no password until the end by confirming with Y, and you will get the following response:
Retype new password:
No password supplied
New password:
Retype new password:
No password supplied
New password:
Retype new password:
No password supplied
passwd: Authentication token manipulation error
passwd: password unchanged
Try again? [y/N] n
Changing the user information for hedgedoc
Enter the new value, or press ENTER for the default
	Full Name []:
	Room Number []:
	Work Phone []:
	Home Phone []:
	Other []:
Is the information correct? [Y/n] Y

Database (PostgreSQL)

  1. The following procedure should automatically create the database, but in our case, we had to provide it manually as follows:
  • move to the postgres user with
su postgres
  • then access the PostgteSQL database with
  • create the hedgedoc database with the command
  • create the hedgedoc user with the password with the command (replace ‘password’ with your own)
ALTER USER hedgedoc password 'password';
  • grant privileges to the hedgedoc user on the hedgedoc database with
GRANT ALL privileges on database hedgedoc to hedgedoc;
  • exit from PostgreSQL with
  • and then type

Download the latest available version and unzip the file.

  1. download the latest available version (in our case 1.9.3)
wget https://github.com/hedgedoc/hedgedoc/releases/download/1.9.3/hedgedoc-1.9.3.tar.gz
  1. unpack the file to a folder (we chose /opt/hedgedoc) and then:
cd /opt 

and then

tar -xzvf hedgedoc-1.9.3.tar.gz


  1. You need to assign permissions to the /op/hedgedoc folder with the following command:
chown -R hedgedoc:hedgedoc /opt/hedgedoc


  1. At this point, move to the /op/hedgedoc folder with
cd /opt/hedgedoc
  1. Run the setup file that will create the config.json file.


  1. You need to generate a secret code to put in the config.json file and then run the command
pwgen -s 64 1

A code will be generated that we will need to copy for use within the config.json file.

  1. Edit the config.json file with
nano config.json

Configuration is critical because the next step will be based on the contents of the config.json file. For convenience, we list the contents of our config.json below

    "production": {
        "domain": "domainname",
        "loglevel": "info",
        "hsts": {
            "enable": true,
            "maxAgeSeconds": 31536000,
            "includeSubdomains": true,
            "preload": true
        "csp": {
            "enable": true,
            "directives": {
            "upgradeInsecureRequests": "auto",
            "addDefaults": true,
            "addDisqus": true,
            "addGoogleAnalytics": false
        "cookiePolicy": "lax",
        "db": {
            "username": "hedgedoc",
            "password": "password",
            "database": "hedgedoc",
            "host": "localhost",
            "port": "5432",
            "dialect": "postgres"
        "sessionSecret": "insert your secret here",
        "port": "3220",
        "email": true,
        "allowEmailRegister": false,
        "protocolUseSSL": true,
        "urlAddPort": false

We deleted the settings related to development, leaving only production because we used our domain name.

In our case, because port 3000 is busy by another service, we changed it to port 3220. If there is no special reason, you can avoid adding the line "port": "3220", because the default port will be 3000.

Build the frontend (possible)

This is an optional step.

Once the configuration is complete, we proceed by running the following command

yarn install --frozen-lockfile

and then the following command

yarn build

Verify the installation

To verify that the installation was successful and there are no errors, inside the /opt/hedgedoc folder you run the following command

NODE_ENV=production yarn start

If everything goes well, the service will be active; otherwise, you will see errors to check.

Setting up the reverse proxy with NGINX.

We set up the reverse proxy with NGINX by following the directions and template available at this page.

Then we created the hedgedoc.conf file as below.

cd /etc/nginx/sites-available

and then

nano hedgedoc.conf

Then we pasted the contents of the NGINX file as we show below

map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
server {
        server_name hedgedoc.example.com;

        location / {
                proxy_set_header Host $host; 
                proxy_set_header X-Real-IP $remote_addr; 
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
                proxy_set_header X-Forwarded-Proto $scheme;

        location /socket.io/ {
                proxy_set_header Host $host; 
                proxy_set_header X-Real-IP $remote_addr; 
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;

N.B.: we left the proxy_pass on port 3000 but - in our case - we had to change it to port 3220.

So, we save the file with CTRL+X and then we redo Y.

At this point we verify that NGINX does not generate errors with the command

nginx -t

If the answer is OK, everything will be set up normally.

At this point we restart the NGINX service with the command

systemctl restart nginx

Create SSL certificate and redirect to HTTPS.

Run the following command

certbot --nginx

Follow the directions, selecting the domain name you intend to use for HedgeDoc.

Set up the systemd service.

The contents of the file for the systemd service are available at this page.

We proceed with creating the file as follows:

  • we move to the /etc/systemd/system folder with the command
cd /etc/systemd/system

Next, we create the file hedgedoc.service with the command

nano hedgedoc.service

We paste the contents of the file. For convenience we report the contents of our hedgedoc.service file.

Description=HedgeDoc - The best platform to write and share markdown.
# Uncomment if you use MariaDB/MySQL
# After=mysql.service
# Uncomment if you use PostgreSQL

ExecStart=/usr/bin/yarn start --production
# CapabilityBoundingSet=
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

# You may have to adjust these settings

# Example: local storage for uploads and SQLite


Next, we save the file with CTRL+X and then repond Y.

At this point, we start the service with the following commands:

systemctl enable hedgedoc.service

and then

systemctl start hedgedoc.service


If all went well, the service will be available by reaching the domain name page we chose for HedgeDoc.

If this resource was helpful, please consider

Follow us on Mastodon

Stay tuned!