Altoplace

Add Comments to Your Hugo Site with Remark42

Update key on a keyboard.
Image by Tumisu from Pixabay

After I moved from WordPress to Hugo , I was missing a commenting system. Actually, I never did receive that many comments when using WordPress, but I still want to give folks the ability to leave comments. I was looking for a simple, self-hosted solution that would work with my VPS. My VPS provider does not permit root access, so that was an additional restriction. My VPS does support using a “rootless” version of Docker, and I learned that Remark42 would work with it.

This post describes how to set up a self-hosted Remark42 commenting system, using Docker, on my Hugo website. My Hugo website and the Remark42 system runs on my Fully-managed VPS (with no root access) provided by Pair .

Privacy-focused Lightweight Commenting Engine

In a nut shell, that describes Remark42 . You can go to their excellent website to see a list of features. Their system is well documented. I am going to hit on the highlights about what I did to integrate their system with my Hugo website. You can also read about how they value privacy. Their system supports various ways to login and leave a comment. You can configure various social logins such as Google and GitHub. You can login via an email address (which is not displayed). And there is even optional anonymous access. I chose to use Google, GitHub, and email access. I do not allow anonymous access; I was afraid that method would create a lot of spam comments.

They also have an active support community that quickly answered my question about Remark42 working in a rootless docker environment . As part of my Pair environment, I am also using a reverse proxy. Pair provides the ability to easily create a reverse proxy for my remark42 hosting domain.

Set Up Your Environment

You need to ensure that docker and docker-compose is working on your host. There are many excellent Docker tutorials on the net, so I will not cover the details of installing and using it. On my Pair host, I was able to install it with one click in my control pannel. You can refer to the Docker Docs to get started. After Docker is installed, you can run docker run hello-world to verify your installation.

Next, you need to configure a subdomain, such as remark42.example.com, to host your commenting system. I enabled “https” and a reverse proxy on my remark42 subdomain. With Pair, this only took a couple of clicks. The reverse proxy maps to http://localhost:8080.

Install the Remark42 Docker Image

You are going to use docker compose to install and manage your Remark42 commenting system (don’t use docker-compose). Do the following to verify that docker compose is installed and working:

$ docker compose version
Docker Compose version v2.29.1

Note, on my host, Docker is running in rootless mode, because my VPS provider does not allow root access.

Next, in my remark42 subdomain root, I created a docker-compose.yml file that defines my Remark42 Docker configuration. The Remark42 GitHub repository provides an example docker-compose.yml file that you can customize to your needs. Here’s what my docker-compose.yml looks like:

name: remark42
networks:
  remark-proxy:
    name: remark-proxy
volumes:
  remark-db:
    name: remark-db

services:
  remark:
    image: umputun/remark42:latest
    container_name: "remark42"
    restart: always

    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

    environment:
      - REMARK_URL=https://remark42.altoplace.com
      - SITE=altoplace
      - SECRET="a long random string"
      - STORE_BOLT_PATH=/srv/var/db
      - BACKUP_PATH=/srv/var/backup
      - EMOJI=true
      - AUTH_ANON=false
      - AUTH_GITHUB_CID="Your GitHub OAUTH ID"
      - AUTH_GITHUB_CSEC="Your GitHub OATH secret"
      - AUTH_GOOGLE_CID="Your Google OATH ID"
      - AUTH_GOOGLE_CSEC="Your Google OATH secret"
      - SMTP_HOST="Your SMTP host"
      - SMTP_PORT=465
      - SMTP_TLS=true
      - SMTP_USERNAME="Your SMTP username"
      - SMTP_PASSWORD="Your SMTP password"
      - AUTH_EMAIL_FROM="Your AUTH email address"
      - NOTIFY_USERS=email
      - NOTIFY_ADMINS=email
      - AUTH_EMAIL_ENABLE=true
      - NOTIFY_EMAIL_FROM="Your NOTIFY email address"
      - ADMIN_SHARED_EMAIL="Your ADMIN email address"
      - ADMIN_SHARED_ID="Your ADMIN shared ID (See below)"

    ports:
      - "127.0.0.1:8080:8080"

    networks:
      - remark-proxy

    volumes:
      - remark-db:/srv/var

You can read about the interface parameters at the Remark42 site. They show the minimal docker-compose.yml file with all the required parameters. I have added a few additional ones for my application. The networks section creates a named internal Docker network; otherwise, Docker creates a long default network name. The volumes section is more important, especially if your Docker system is running in a rootless mode. I am creating a named volume to store comment data instead of a bind mount as shown in the Docker minimal configuration.

Docker creates volume data under docker ownership. I discovered this when I was experimenting with Remark42, using the example configuration file. I could not delete the example volume bind-mounted data because I did not have root access. I had to contact support and ask them to delete the data for me. When using the named volume mode, you can use docker compose commands to manage the volume data, including deleting it if you need to.

To generate a “a long random string” for the SECRET parameter, I like using the Homebrew pwgen tool:

$ pwgen -s 64 1
H8qRx8F8UnypUqTU8zep8UJFH5KSm6DQHTGSCBwonilj5kSXpzzn5SfqIPAay1JQ

You can adjust the string length (64) as desired.

The Remark42 documentation provides detailed procedures for configuring OAuth providers, so I will not repeat it here.

The “ports” definition allows access to the Remark42 instance on the local host, including the reverse proxy. But it blocks access from an external IP.

The ADMIN_SHARED_ID was a bit tricky to get. After you have your Remark42 commenting system working with Hugo, login, for example with GitHub, and click on your user name. You will see a slide-in screen on the right with your GitHub Admin shared ID. Then you can add the ADMIN_SHARED_ID to your docker compose file (be sure to stop and start your docker container after making any changes to the config file).

Change directory to the location of your docker compose configuration and pull the Remark42 image, using docker compose:

$ l
docker-compose.yml

$ docker compose pull
[+] Pulling 18/18
remark Pulled                      17.1s
  4abcf2066143 Pull complete        0.7s
  d84f2b415aa0 Pull complete        0.9s
  c249752ae588 Pull complete        1.1s
  2a2c873dc54c Pull complete        1.2s
  21af0f4b4218 Pull complete        1.4s
  3282bb3bf5c1 Pull complete        1.5s
  2d41708e6ec1 Pull complete        3.0s
  4f4fb700ef54 Pull complete        3.4s
  c93785f4bb21 Pull complete        3.8s
  ad5a2b1d4553 Pull complete        4.5s
  b69781e4c45d Pull complete        5.1s
  ae0a1c16f628 Pull complete        5.6s
  79ce655ebebb Pull complete        6.1s
  d90e0dadcedb Pull complete       13.2s
  a652ff4c12f2 Pull complete       13.8s
  68e9b2f0bbd2 Pull complete       15.0s
  13560e2b8964 Pull complete       15.5s

You will see each subimage being pulled down and installed. Your final pull state should look simalar to the above.

You can verify that the Remar42 image is installed:

$ docker image ls
REPOSITORY                     TAG                 IMAGE ID       CREATED       SIZE
umputun/remark42               latest              274272164479   5 weeks ago   81.2MB

Start the Remark42 Docker Container

Start the Remark42 server with:

$ docker compose up -d
[+] Running 3/3
 Network remark-proxy  Created                     0.3s
 Volume "remark-db"    Created                     0.0s
 Container remark42    Started

The first time when you run this command, the persistent volume, remark-db, is created. You can verify that the remark42 container is running:

$ docker ps
CONTAINER ID   IMAGE                                            COMMAND                  CREATED         STATUS                   PORTS                      NAMES
bdc29d4cb80a   umputun/remark42:latest                          "/init.sh /srv/remar…"   4 minutes ago   Up 4 minutes (healthy)   127.0.0.1:8080->8080/tcp   remark42

Your want to verify that the STATUS is “healthy”. You can also run the following “ping” command, via the curl command, to verify that you can communicate with the Remark42 container:

$ curl http://127.0.0.1:8080/ping
pong

You want to see the pong response.

You can stop and start the Remark42 with these commands:

$ docker stop remark42
remark42

$ docker start remark42
remark42

Integrate Remark42 with Hugo

Exactly how you do that will depend on which Hugo theme you are using. Some themes may already have Remark42 support built in. I added the following to just after the {{ .Content }} section in layouts/_default/single.html:

{{ .Content }}

{{ if $.Param "remark42.enabled" | default false }}{{ partial "remark42.html" . }}{{ end -}}

In my Hugo site configuration file (hugo.yaml or config.yaml), I added:

params:
  remark42:
    enabled: true
    host: "https://remark42.altoplace.com"
    site: "altoplace"
    locale: "en"

Note: The site parameter must match the one that you used for the SITE variable in your docker-compose.yml file. Also, be sure that host matches your REMARK_URL in docker compose configuration. You get the idea.

Finally, here is my layouts/partials/remark42.html file:

{{- with .Site.Params.remark42 -}}
<hr>
<p><b>Your comments are welcome!</b> <i>You can use your email address to Sign In (your email address is not publicly displayed), or you can use your Google or GitHub account. All comment data is hosted on Altoplace and is not tracked; please refer to the <a href='https://remark42.com' target="_blank"><b>Remark42</b></a> <a href='https://remark42.com/privacy/' target="_blank">Privacy Policy</a> for more information.</i></p>
<div id="remark42"></div>
<script>
    var remark_config = {
        host: '{{ .host }}',
        site_id: '{{ .site }}',
        components: ['embed'],
        url: '{{ $.Permalink }}',
    };

    !function(e, n) {
        for (var o = 0; o < e.length; o++) {
            var r = n.createElement('script'),
            c = '.js',
            d = n.head || n.body;
            'noModule' in r ? (r.type = 'module', c = '.mjs') : r.async = !0, r.defer = !0, r.src = remark_config.host + '/web/' + e[o] + c, d.appendChild(r)
        }
    }(remark_config.components || ['embed'], document);

</script>
{{- end -}}

You can look below to see the final results. Better yet, try leaving a comment!

Final Thoughts

I finally have a commenting system working with Hugo! It’s self-hosted, so I am not paying for an external 3rd party service. The comments stay on my server, under my control, so they remain private. I hope that I provided enough information for you to add commenting to your Hugo site. Please, let me know if something is not clear or you have a question. I will be happy to try to answer it. Just leave a comment below. 😄


Your comments are welcome! You can use your email address to Sign In (your email address is not publicly displayed), or you can use your Google or GitHub account. All comment data is hosted on Altoplace and is not tracked; please refer to the Remark42 Privacy Policy for more information.