Skip to main content
Isometric tech illustration showing a Remark42 comment system hosted on a Raspberry Pi, connected via glowing data streams to a Hugo website interface, set against a dark background with security icons.

How to Self-Host Remark42 Comments with Docker & Hugo

5 min 939 words

When you embark on the exciting journey of creating a blog, you quickly face a crucial question: how to interact with your audience? This post addresses that very topic. I will explain how to integrate Remark42 as a comment system for a Hugo blog (using the Stack theme as an example). We will cover self-hosting it on a Raspberry Pi using Docker and Nginx.

Why choose Remark42?

In the early days of blogging, many of us—myself included—opted for Disqus. It’s incredibly easy to set up: create an account, paste a snippet, and you’re live in under an hour.

However, when I started looking into Content Security Policy (CSP) headers and privacy, I realized that Disqus wasn’t exactly treating user data with the respect I hoped for. Since data privacy is a topic I pay close attention to, I started looking for alternatives. My criteria were strict:

  • Open-source solution.

  • Self-hosted (to maintain control over the data).

  • Actively maintained with regular updates.

  • Compatible with my Hugo theme.

I finally settled on Remark42. According to their official website, Remark42 focuses on privacy: no tracking, no third-party analytics, and all sensitive data is encrypted. It offers multiple social login options (Google, Twitter, GitHub, etc.), email authentication, and even anonymous commenting. Best of all, it is “fully dockerized and can be deployed in a single command.”

Server-side Setup with Docker Compose

For this setup, we are hosting Remark42 on a Raspberry Pi via Docker, using Nginx as a reverse proxy with an SSL certificate generated by Let’s Encrypt.

From the Remark42 installation page, you can retrieve the basic docker-compose.yml. Below is a merged configuration example, assuming you might be running other services (like Nginx) on the same instance.

version: "3"

services:
  # ... other services ...

  remark42:
    build: .
    image: umputun/remark42:latest
    container_name: "remark42"
    restart: always
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
    environment:
      - REMARK_URL=${remark-url}
      - SITE=${site}
      - SECRET=${secret}
      - AUTH_ANON=${auth-anon}
      - AUTH_SAME_SITE=none
      - ALLOWED_HOSTS=${website-url}
      - AUTH_GOOGLE_CID=${auth-google-cid}
      - AUTH_GOOGLE_CSEC=${auth-google-csec}
      - AUTH_GITHUB_CID=${auth-github-cid}
      - AUTH_GITHUB_CSEC=${auth-github-csec}
      - AUTH_EMAIL_ENABLE=${auth-email-enable}
      - ADMIN_SHARED_ID=${admin-shared-id}
      - NOTIFY_ADMINS=email
      - ADMIN_SHARED_EMAIL=${admin-shared-email}
      - SMTP_HOST=${smtp-host}
      - SMTP_PORT=${smtp-port}
      - SMTP_TLS=${smtp-tls}
      - SMTP_USERNAME=${smtp-username}
      - SMTP_PASSWORD=${smtp-password}
      - AUTH_EMAIL_FROM=${auth-email-from}
      - NOTIFY_EMAIL_FROM=${notify-email-from}
    volumes:
      - ./remark42:/srv/var

  nginx:
    image: nginx:latest
    container_name: "nginx"
    restart: always
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:z,ro
      - /etc/letsencrypt/:/etc/letsencrypt/:z,ro
    ports:
      - 8080:80
      - 8443:443

Let’s break down the environment variables (${value}) and how to configure them.

Docker Configuration Details

  • ${remark-url}: The domain pointing to your Remark42 instance. You should define a subdomain (e.g., comments.your-blog.com) and add a DNS entry routing traffic to your Raspberry Pi. This must be publicly accessible for the frontend to reach the backend and for OAuth providers (Google/GitHub) to work.

  • ${website-url}: The URL of the blog where comments will be displayed (e.g., https://www.your-blog.com).

  • ${site}: An identifier for your website. If you only host comments for one site, default works fine.

  • ${secret}: Used to sign JSON Web Tokens (JWT). This must be a long, random, high-entropy string.

Pro Tip: You can generate a robust secret directly in your terminal:

Bash

tr -dc 'A-Za-z0-9!?%=' < /dev/urandom | head -c 30
  • ${admin-shared-id}: A comma-separated list of admin user IDs.

    • How to find your ID: Log in to Remark42 on your site. Click your profile picture to open the sidebar. Copy the long string under your name (it starts with the provider, e.g., github_...).

How to get the user ID of a logged-in user

Email Notifications Setup

To receive email alerts when new comments are posted:

  • ${admin-shared-email}: The administrator’s email address.

  • ${smtp-host}: Your email provider’s SMTP address (e.g., smtp.gmail.com or smtp-mail.outlook.com).

  • ${smtp-port}: Usually 587 or 465. Check your provider’s docs or this Cloudflare article.

  • ${smtp-tls}: Set to true (standard for most modern providers).

  • ${smtp-username} / ${smtp-password}: Your credentials for the SMTP account.

  • ${notify-email-from}: The email address that will appear as the “Sender” of the notification.

Authentication Methods

Remark42 supports many providers (Google, Twitter, Facebook, Microsoft, GitHub, Yandex, etc.). Below are the settings for the ones I used.

  • ${auth-anon}: Set to true to allow anonymous comments, false otherwise.

  • ${auth-email-enable}: Set to true to allow login via email magic links. If enabled, you must configure the SMTP settings above and define ${auth-email-from}.

Example of email containing a token sent during the connection processExample of email containing a token sent during the connection process

Configuring OAuth Providers

While anonymous and email login are great, social login reduces friction for users.

GitHub Authentication

This is the easiest to configure.

  1. Log in to GitHub and go to Settings > Developer Settings > OAuth Apps > New OAuth App.

  2. Application Name: “Remark42 Comments” (or similar).

  3. Homepage URL: Your blog URL (e.g., https://your-blog.com).

  4. Authorization callback URL: Your Remark42 domain + /auth/github/callback (e.g., https://comments.your-blog.com/auth/github/callback).

  5. Register the application.

  6. Copy the Client ID into ${auth-github-cid}.

  7. Generate a Client Secret and paste it into ${auth-github-csec}.

The creation form for an OAuth App on GitHub
The creation form for an OAuth App on GitHub

Google Authentication

Disclaimer: This process is more complex and may require verifying your domain and adding a privacy policy to your site.

  1. Go to the Google Cloud Console.

  2. Create a New Project.

  3. Navigate to APIs & Services > OAuth consent screen.

  4. Select External and fill in the required details (App name, Support email, Authorized domains).

    • Note: You don’t need to add scopes for basic login.
  5. Go to Credentials > Create Credentials > OAuth client ID.

    • Application type: Web application.

    • Authorized JavaScript origins: Your blog URL.

    • Authorized redirect URIs: Your Remark42 domain + /auth/google/callback.

  6. Copy the Client ID and Secret into your docker-compose file.

The OAuth client ID menuThe OAuth client ID menu

Important: You must click “PUBLISH APP” on the OAuth consent screen. Google may require a review process, which can take weeks. Ensure your site has a visible Privacy Policy. Conveniently, Remark42 hosts a default policy at ${remark-url}/web/privacy.html.

Integrating Remark42 into Hugo

Once the backend is running, integrating it into Hugo (specifically the hugo-theme-stack) is straightforward. Edit your params.toml:

[comments.remark42]
host = "https://comments.your-blog.com" # Your ${remark-url}
site = "default" # Your ${site} value
locale = "en" # or "fr", etc.

The comments.remark42 section on params.tomlThe [comments.remark42] section on params.toml

Deploy your site, and you should see the comment section appear!

Final Advice

Self-hosting a comment system is rewarding but has a learning curve. I recommend starting small: get the Docker container running with just Anonymous or Email authentication first. Once that works reliably, tackle the complexity of Google or GitHub OAuth. You don’t need every feature enabled on day one—build it up iteratively.