108 lines
No EOL
33 KiB
HTML
108 lines
No EOL
33 KiB
HTML
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt | AlipourIm journeys</title>
|
||
<meta name=keywords content="hedgedoc,docker,nginx,letsencrypt,self-hosting"><meta name=description content="HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.
|
||
|
||
Prerequisites
|
||
|
||
A Linux server with Docker and Docker Compose
|
||
A domain name pointing to your server (e.g. notes.alipourimjourneys.ir)
|
||
Nginx installed for reverse proxying
|
||
Certbot for Let’s Encrypt certificates
|
||
|
||
|
||
1. Create the project directory
|
||
mkdir ~/hedgedoc && cd ~/hedgedoc
|
||
|
||
2. Create .env
|
||
POSTGRES_PASSWORD=ChangeThisStrongPassword
|
||
HD_DOMAIN=notes.alipourimjourneys.ir
|
||
Generate a strong password with:"><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt"><meta property="og:description" content="HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.
|
||
Prerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Let’s Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc && cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:"><meta property="og:locale" content="en"><meta property="og:type" content="article"><meta property="article:section" content="posts"><meta property="article:published_time" content="2025-08-29T00:00:00+00:00"><meta property="article:modified_time" content="2025-08-29T00:00:00+00:00"><meta property="article:tag" content="Hedgedoc"><meta property="article:tag" content="Docker"><meta property="article:tag" content="Nginx"><meta property="article:tag" content="Letsencrypt"><meta property="article:tag" content="Self-Hosting"><meta property="og:image" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/images/hedgedoc-cover.png"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/images/hedgedoc-cover.png"><meta name=twitter:title content="Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt"><meta name=twitter:description content="HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.
|
||
|
||
Prerequisites
|
||
|
||
A Linux server with Docker and Docker Compose
|
||
A domain name pointing to your server (e.g. notes.alipourimjourneys.ir)
|
||
Nginx installed for reverse proxying
|
||
Certbot for Let’s Encrypt certificates
|
||
|
||
|
||
1. Create the project directory
|
||
mkdir ~/hedgedoc && cd ~/hedgedoc
|
||
|
||
2. Create .env
|
||
POSTGRES_PASSWORD=ChangeThisStrongPassword
|
||
HD_DOMAIN=notes.alipourimjourneys.ir
|
||
Generate a strong password with:"><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Posts","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"},{"@type":"ListItem","position":2,"name":"Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/"}]}</script><script type=application/ld+json>{"@context":"https://schema.org","@type":"BlogPosting","headline":"Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt","name":"Self-Hosting HedgeDoc with Docker \u002b Nginx \u002b Let\u0027s Encrypt","description":"HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.\nPrerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Let’s Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc \u0026amp;\u0026amp; cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:\n","keywords":["hedgedoc","docker","nginx","letsencrypt","self-hosting"],"articleBody":"HedgeDoc is an open-source collaborative Markdown editor. Think Google Docs for Markdown: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.\nPrerequisites A Linux server with Docker and Docker Compose A domain name pointing to your server (e.g. notes.alipourimjourneys.ir) Nginx installed for reverse proxying Certbot for Let’s Encrypt certificates 1. Create the project directory mkdir ~/hedgedoc \u0026\u0026 cd ~/hedgedoc 2. Create .env POSTGRES_PASSWORD=ChangeThisStrongPassword HD_DOMAIN=notes.alipourimjourneys.ir Generate a strong password with:\nopenssl rand -base64 32 3. Create docker-compose.yml version: \"3.9\" services: db: image: postgres:16 environment: POSTGRES_USER: hedgedoc POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: hedgedoc volumes: - db:/var/lib/postgresql/data restart: unless-stopped hedgedoc: image: quay.io/hedgedoc/hedgedoc:1.10.2 depends_on: - db environment: CMD_DB_URL: postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc CMD_DOMAIN: ${HD_DOMAIN} CMD_PROTOCOL_USESSL: \"true\" CMD_URL_ADDPORT: \"false\" CMD_PORT: \"3000\" CMD_EMAIL: \"true\" CMD_ALLOW_EMAIL_REGISTER: \"false\" volumes: - uploads:/hedgedoc/public/uploads ports: - \"127.0.0.1:3000:3000\" restart: unless-stopped volumes: db: uploads: Bring it up:\ndocker compose up -d 4. Get a Let’s Encrypt certificate Request a cert with a DNS challenge:\nsudo certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email Add the TXT record certbot asks for, wait for DNS to propagate, then continue.\nCertificates will be in:\n/etc/letsencrypt/live/notes.alipourimjourneys.ir/ 5. Configure Nginx Create /etc/nginx/sites-available/notes.alipourimjourneys.ir:\nserver { server_name notes.alipourimjourneys.ir; listen 80; listen [::]:80; return 301 https://$host$request_uri; } server { server_name notes.alipourimjourneys.ir; listen 443 ssl http2; listen [::]:443 ssl http2; ssl_certificate /etc/letsencrypt/live/notes.alipourimjourneys.ir/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/notes.alipourimjourneys.ir/privkey.pem; location / { proxy_pass http://127.0.0.1:3000; 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 https; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \"upgrade\"; } } Enable the config and reload Nginx:\nsudo ln -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/ sudo nginx -t \u0026\u0026 sudo systemctl reload nginx 6. Create users Because we disabled self-registration, create accounts manually:\ndocker compose exec hedgedoc ./bin/manage_users --add alice@example.com You’ll be prompted for a password.\nTo reset a password later:\ndocker compose exec hedgedoc ./bin/manage_users --reset alice@example.com 7. Done! Visit https://notes.alipourimjourneys.ir and log in with the user you created. You now have your own collaborative Markdown editor 🎉\nExtras:\nBackups: dump the PostgreSQL database and save the uploads volume. Upgrades: docker pull quay.io/hedgedoc/hedgedoc:latest \u0026\u0026 docker compose up -d. Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more. ","wordCount":"368","inLanguage":"en","image":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/images/hedgedoc-cover.png","datePublished":"2025-08-29T00:00:00Z","dateModified":"2025-08-29T00:00:00Z","author":{"@type":"Person","name":"Iman Alipour"},"mainEntityOfPage":{"@type":"WebPage","@id":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/"},"publisher":{"@type":"Organization","name":"AlipourIm journeys","logo":{"@type":"ImageObject","url":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico"}}}</script></head><body id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><article class=post-single><header class=post-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a> » <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/>Posts</a></div><h1 class="post-title entry-hint-parent">Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt</h1><div class=post-meta><span title='2025-08-29 00:00:00 +0000 UTC'>August 29, 2025</span> · 2 min · Iman Alipour
|
||
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/#isso-thread class=isso-comments-link>Comments</a></span></div></header><figure class=entry-cover><img loading=eager src=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/images/hedgedoc-cover.png alt="HedgeDoc collaborative editing"><figcaption>Collaborative Markdown editing with HedgeDoc</figcaption></figure><div class=post-content><p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.</p><hr><h2 id=prerequisites>Prerequisites<a hidden class=anchor aria-hidden=true href=#prerequisites>#</a></h2><ul><li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li><li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li><li><strong>Nginx</strong> installed for reverse proxying</li><li><strong>Certbot</strong> for Let’s Encrypt certificates</li></ul><hr><h2 id=1-create-the-project-directory>1. Create the project directory<a hidden class=anchor aria-hidden=true href=#1-create-the-project-directory>#</a></h2><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>mkdir ~/hedgedoc <span style=color:#f92672>&&</span> cd ~/hedgedoc</span></span></code></pre></div><hr><h2 id=2-create-env>2. Create <code>.env</code><a hidden class=anchor aria-hidden=true href=#2-create-env>#</a></h2><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-env data-lang=env><span style=display:flex><span>POSTGRES_PASSWORD<span style=color:#f92672>=</span>ChangeThisStrongPassword
|
||
</span></span><span style=display:flex><span>HD_DOMAIN<span style=color:#f92672>=</span>notes.alipourimjourneys.ir</span></span></code></pre></div><p>Generate a strong password with:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>openssl rand -base64 <span style=color:#ae81ff>32</span></span></span></code></pre></div><hr><h2 id=3-create-docker-composeyml>3. Create <code>docker-compose.yml</code><a hidden class=anchor aria-hidden=true href=#3-create-docker-composeyml>#</a></h2><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=display:flex><span><span style=color:#f92672>version</span>: <span style=color:#e6db74>"3.9"</span>
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span><span style=color:#f92672>services</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>db</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>image</span>: <span style=color:#ae81ff>postgres:16</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>environment</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>POSTGRES_USER</span>: <span style=color:#ae81ff>hedgedoc</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>POSTGRES_PASSWORD</span>: <span style=color:#ae81ff>${POSTGRES_PASSWORD}</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>POSTGRES_DB</span>: <span style=color:#ae81ff>hedgedoc</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>volumes</span>:
|
||
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>db:/var/lib/postgresql/data</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>restart</span>: <span style=color:#ae81ff>unless-stopped</span>
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>hedgedoc</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>image</span>: <span style=color:#ae81ff>quay.io/hedgedoc/hedgedoc:1.10.2</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>depends_on</span>:
|
||
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>db</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>environment</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_DB_URL</span>: <span style=color:#ae81ff>postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_DOMAIN</span>: <span style=color:#ae81ff>${HD_DOMAIN}</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_PROTOCOL_USESSL</span>: <span style=color:#e6db74>"true"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_URL_ADDPORT</span>: <span style=color:#e6db74>"false"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_PORT</span>: <span style=color:#e6db74>"3000"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_EMAIL</span>: <span style=color:#e6db74>"true"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>CMD_ALLOW_EMAIL_REGISTER</span>: <span style=color:#e6db74>"false"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>volumes</span>:
|
||
</span></span><span style=display:flex><span> - <span style=color:#ae81ff>uploads:/hedgedoc/public/uploads</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>ports</span>:
|
||
</span></span><span style=display:flex><span> - <span style=color:#e6db74>"127.0.0.1:3000:3000"</span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>restart</span>: <span style=color:#ae81ff>unless-stopped</span>
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span><span style=color:#f92672>volumes</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>db</span>:
|
||
</span></span><span style=display:flex><span> <span style=color:#ae81ff>uploads:</span></span></span></code></pre></div><p>Bring it up:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>docker compose up -d</span></span></code></pre></div><hr><h2 id=4-get-a-lets-encrypt-certificate>4. Get a Let’s Encrypt certificate<a hidden class=anchor aria-hidden=true href=#4-get-a-lets-encrypt-certificate>#</a></h2><p>Request a cert with a <strong>DNS challenge</strong>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email</span></span></code></pre></div><p>Add the TXT record certbot asks for, wait for DNS to propagate, then continue.<br>Certificates will be in:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>/etc/letsencrypt/live/notes.alipourimjourneys.ir/</span></span></code></pre></div><hr><h2 id=5-configure-nginx>5. Configure Nginx<a hidden class=anchor aria-hidden=true href=#5-configure-nginx>#</a></h2><p>Create <code>/etc/nginx/sites-available/notes.alipourimjourneys.ir</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-nginx data-lang=nginx><span style=display:flex><span><span style=color:#66d9ef>server</span> {
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>server_name</span> <span style=color:#e6db74>notes.alipourimjourneys.ir</span>;
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#ae81ff>80</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#e6db74>[::]:80</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>return</span> <span style=color:#ae81ff>301</span> <span style=color:#e6db74>https://</span>$host$request_uri;
|
||
</span></span><span style=display:flex><span>}
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span><span style=color:#66d9ef>server</span> {
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>server_name</span> <span style=color:#e6db74>notes.alipourimjourneys.ir</span>;
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#ae81ff>443</span> <span style=color:#e6db74>ssl</span> <span style=color:#e6db74>http2</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#e6db74>[::]:443</span> <span style=color:#e6db74>ssl</span> <span style=color:#e6db74>http2</span>;
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>ssl_certificate</span> <span style=color:#e6db74>/etc/letsencrypt/live/notes.alipourimjourneys.ir/fullchain.pem</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>ssl_certificate_key</span> <span style=color:#e6db74>/etc/letsencrypt/live/notes.alipourimjourneys.ir/privkey.pem</span>;
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>location</span> <span style=color:#e6db74>/</span> {
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_pass</span> <span style=color:#e6db74>http://127.0.0.1:3000</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>Host</span> $host;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>X-Real-IP</span> $remote_addr;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>X-Forwarded-Proto</span> <span style=color:#e6db74>https</span>;
|
||
</span></span><span style=display:flex><span>
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_http_version</span> <span style=color:#ae81ff>1</span><span style=color:#e6db74>.1</span>;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>Upgrade</span> $http_upgrade;
|
||
</span></span><span style=display:flex><span> <span style=color:#f92672>proxy_set_header</span> <span style=color:#e6db74>Connection</span> <span style=color:#e6db74>"upgrade"</span>;
|
||
</span></span><span style=display:flex><span> }
|
||
</span></span><span style=display:flex><span>}</span></span></code></pre></div><p>Enable the config and reload Nginx:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo ln -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/
|
||
</span></span><span style=display:flex><span>sudo nginx -t <span style=color:#f92672>&&</span> sudo systemctl reload nginx</span></span></code></pre></div><hr><h2 id=6-create-users>6. Create users<a hidden class=anchor aria-hidden=true href=#6-create-users>#</a></h2><p>Because we disabled self-registration, create accounts manually:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>docker compose exec hedgedoc ./bin/manage_users --add alice@example.com</span></span></code></pre></div><p>You’ll be prompted for a password.<br>To reset a password later:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>docker compose exec hedgedoc ./bin/manage_users --reset alice@example.com</span></span></code></pre></div><hr><h2 id=7-done>7. Done!<a hidden class=anchor aria-hidden=true href=#7-done>#</a></h2><p>Visit <a href=https://notes.alipourimjourneys.ir>https://notes.alipourimjourneys.ir</a> and log in with the user you created. You now have your own collaborative Markdown editor 🎉</p><hr><p><strong>Extras:</strong></p><ul><li>Backups: dump the PostgreSQL database and save the <code>uploads</code> volume.</li><li>Upgrades: <code>docker pull quay.io/hedgedoc/hedgedoc:latest && docker compose up -d</code>.</li><li>Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more.</li></ul></div><footer class=post-footer><ul class=post-tags><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hedgedoc/>Hedgedoc</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/docker/>Docker</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/nginx/>Nginx</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/letsencrypt/>Letsencrypt</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/self-hosting/>Self-Hosting</a></li></ul><nav class=paginav><a class=prev href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/augest_2025/><span class=title>« Prev</span><br><span>Augest '25</span>
|
||
</a><a class=next href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/matrix_setup/><span class=title>Next »</span><br><span>Self-hosting Matrix + Element Call with LiveKit: from zero to working (and the [not so!!] fun debugging along the way)</span></a></nav></footer><div class=comments-switch style="display:flex;gap:.5rem;margin:.5rem 0 1rem"><button id=btn-isso type=button aria-pressed=true>Anonymous (Isso)</button>
|
||
<button id=btn-giscus type=button aria-pressed=false>GitHub (Giscus)</button>
|
||
<a href=https://github.com/AlipourIm/blog-comments/discussions/categories/comments target=_blank rel=noopener>Open on GitHub ↗</a></div><div id=panel-isso><section id=isso-thread></section><script src=/isso/js/embed.min.js data-isso=/isso data-isso-css=true data-isso-lang=en data-isso-max-comments-nested=5 data-isso-sorting=newest async></script><small class=isso-powered style="display:block;margin:.5rem 0;color:var(--secondary,#888)">Comments powered by <a href=https://isso-comments.de target=_blank rel=noopener>Isso</a></small></div><div id=panel-giscus style=display:none><section id=giscus-thread></section></div><script>(function(){var t=document.getElementById("btn-isso"),n=document.getElementById("btn-giscus"),o=document.getElementById("panel-isso"),i=document.getElementById("panel-giscus"),s=!1;function e(e){var a,r=e==="isso";o.style.display=r?"":"none",i.style.display=r?"none":"",t.setAttribute("aria-pressed",r),n.setAttribute("aria-pressed",!r),!r&&!s&&(a=document.createElement("script"),a.src="https://giscus.app/client.js",a.async=!0,a.crossOrigin="anonymous",a.setAttribute("data-repo","AlipourIm/blog-comments"),a.setAttribute("data-repo-id","R_kgDOQGARyA"),a.setAttribute("data-category","Comments"),a.setAttribute("data-category-id","DIC_kwDOQGARyM4Cw3x-"),a.setAttribute("data-mapping","pathname"),a.setAttribute("data-strict","0"),a.setAttribute("data-reactions-enabled","1"),a.setAttribute("data-emit-metadata","0"),a.setAttribute("data-input-position","bottom"),a.setAttribute("data-theme","preferred_color_scheme"),a.setAttribute("data-lang","en"),document.getElementById("giscus-thread").appendChild(a),s=!0)}t.addEventListener("click",function(){e("isso")}),n.addEventListener("click",function(){e("giscus")}),e("isso")})()</script></article></main><footer class=footer><span>© 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
|
||
<span>Powered by
|
||
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
|
||
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
|
||
</a><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
|
||
<span>RSS</span>
|
||
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script><script>document.querySelectorAll("pre > code").forEach(e=>{const n=e.parentNode.parentNode,t=document.createElement("button");t.classList.add("copy-code"),t.innerHTML="copy";function s(){t.innerHTML="copied!",setTimeout(()=>{t.innerHTML="copy"},2e3)}t.addEventListener("click",t=>{if("clipboard"in navigator){navigator.clipboard.writeText(e.textContent),s();return}const n=document.createRange();n.selectNodeContents(e);const o=window.getSelection();o.removeAllRanges(),o.addRange(n);try{document.execCommand("copy"),s()}catch{}o.removeRange(n)}),n.classList.contains("highlight")?n.appendChild(t):n.parentNode.firstChild==n||(e.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName=="TABLE"?e.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(t):e.parentNode.appendChild(t))})</script></body></html> |