Introduction#
In the article "What Has Changed in My Blog in 2024", I introduced the blog system I built using a Serverless platform and some open-source projects, and I started this series of tutorials to document the entire process of building and deploying.
This article is about the comment system solution.
Iteration of Comment Systems#
I often feel that comments are not just a communication interaction between readers and authors; their content itself is also part of the article. In fact, some comments and discussions can often be more valuable than the article itself. Therefore, I have always placed great importance on the comment system and have been reluctant to trust third-party hosted services. I do not want any censorship, and I want the style to be as simple as possible and consistent with my blog's style.
During the development of the blog, the comment system solution has also gone through several iterations. Regarding the types and choices of comment systems, I really like the developer reorx who has detailed introductions in "Changing Blog Comment Systems", and I won't elaborate further. This article focuses more on personal experience and the detailed building process.
Disqus#
The first comment system I used was the notorious Disqus, a bulky and privacy-invasive comment system. It loaded slowly, and the free version often came with ads, which was hard to tolerate. Moreover, at that time, there were basically no comments, so there was no migration burden, and I abandoned it shortly after.
Utterances#
Then I switched to another comment system based on GitHub issues called Utterances. It generates an issue for each article, and users can comment on the issue by authorizing GitHub login. The advantage of this method is that it only requires authorizing an utterances-bot for management, without needing to deploy services or maintain databases. However, after using it for a while, I found several shortcomings:
- Comment management is based on the GitHub API, which may become unstable if the interface changes or if there are restrictions on this type of comment system.
- Readers must authorize GitHub login, which is inconvenient for non-technical users or readers using mobile devices.
- It pollutes the Issues records of the GitHub repository, making it inconvenient to migrate to other systems later.
Cusdis + Supabase + Vercel#
Cusdis is an open-source comment system created by Randy that emphasizes data privacy. It is very lightweight, approximately only 5kb after gzipping. From its name, it is clear that it is a replacement for the unbearable Disqus, and it even supports importing historical data from Disqus, which is very considerate.
I started using it in mid-2021, and it has been running steadily for three years now, except for some initial deployment platform troubles due to Heroku and Railway charging fees. However, I have encountered some issues during use:
- Due to some modifications made by WeChat's built-in browser, the comment component is not visible when opening the blog from WeChat chats/conversations.
- Although email input is supported, it does not allow subscribing to comment replies.
- Comments need to be manually reviewed by the administrator, but the comment notification TG Bot often fails, causing missed comments.
Overall, it is still a highly recommended solution today: lightweight, easy to self-deploy, and with a simple and beautiful style. For the setup tutorial, refer to "Lightweight Open Source Free Blog Comment System Solution (Cusdis + Railway)".
Since Railway has canceled the Free Plan since last August, if you still want to use it completely for free, you can deploy the main project for free using Vercel/Netlify/Zeabur and deploy a free PostgreSQL database instance on Supabase, passing the link as an environment variable into the Cusdis service. The other processes are similar.
Additionally, because its core functionality has not been updated for a long time, it seems a bit rudimentary compared to other more mature comment systems. However, I have adhered to the principle of "good enough" and have not considered migration or updates, only participating in some development of the Cusdis V2 version while learning front-end at one point, but it didn't last long.
Due to repeated failures during the Vercel deployment upgrade in April, I did not receive comments for nearly a few weeks. Coupled with some functional requirements, I decided to migrate and explore new solutions.
Remark42 + fly.io#
After researching, I chose Remark42 as the final choice mentioned by reorx in "Changing Blog Comment Systems".
In terms of configuration options, it is much richer than Cusdis. I have configured several common social account logins (GitHub, Twitter, Telegram, email), allowed anonymous comments, supported email subscription reply notifications, and set up TG bot notifications. It is deployed on fly.io, using a single Go binary + a single database file, which is a very comfortable solution. For a more detailed introduction and advantages of Remark42, refer to the article mentioned above.
Although Remark42 provides some migration solutions, it does not support the Cusdis I used. Fortunately, it is written in Golang, and I added migration logic myself, seamlessly migrating the 438 comments accumulated over the years.
Remark42 + fly.io Deployment Instructions#
The Remark42 + fly.io solution only involves a single service, using boltdb mounted in a volume for the database, and all operations are within fly.io's Free Plan.
Below, I will introduce how to build this free comment system from scratch.
Remark42's code is open source — "GitHub - umputun/remark42", and it provides an officially maintained image with clear and readable documentation, allowing for configuration based on actual needs.
Install flyctl
Command Line Tool#
fly.io differs significantly from the Railway, Zeabur, and others I previously used in that most operations are based on command line and configuration files rather than being managed through a web interface. Therefore, the first step is to install the flyctl
command line tool according to the documentation.
For example, on macOS, I used brew
to install:
brew install flyctl
Authorization Login#
Open the terminal tool and use the following command to authorize login:
flyctl auth login
Log in or create an account on the web, and after completing, click Continue as xxx
to finish the authorization login for the flyctl
command line.
Create Application Directory#
Since I usually manage configurations manually rather than using the official template, I will create a directory like remark42-on-fly
and place all configuration files, environment variables, etc., in this path.
I will use VS Code for editing (you can also use vim or other editors/IDEs).
Configuration File#
Fly.io primarily uses .toml
format configuration files for service management. Below is the configuration file corresponding to the service I deployed:
app = 'yu-remark42-01'
primary_region = 'hkg'
[build]
image = 'umputun/remark42:latest'
[[mounts]]
source = 'remark42_data_01'
destination = '/srv/var'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1
processes = ['app']
[env]
REMARK_URL = 'https://yu-remark42-01.fly.dev/'
SECRET = 'remark42-secret'
SITE= 'remark42-demo'
ADMIN_SHARED_ID= ''
[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 256
Here is a detailed explanation of the configuration:
app
: Application name, here I usedyu-remark42-01
, which can be modified according to your actual situation.primary_region
: Deployment region, which can be selected from this list. I chose Hong Kong.[build]
: This section mainly contains service image-related configurations.image
: Service image, using the officialumputun/remark42:latest
. You can specify a tag version if needed.
[[mounts]]
: This section mainly contains the configuration for mounting data volumes. Since Remark42 uses a boltdb database, persistent storage is needed.source
: Data volume name, here I usedremark42_data_01
.destination
: Mount directory, here I mounted it to/srv/var
, which is the default data storage directory for Remark42.
[http_service]
: This section mainly contains service-related configurations.internal_port
: Internal port for the service, using 8080.force_https
: Force HTTPS.auto_stop_machines
: Set tofalse
.auto_start_machines
: Set totrue
, meaning it will start automatically.min_machines_running
: Minimum number of running machines, set to 1.processes
: Service process, set toapp
.
[env]
: Configure environment variables.REMARK_URL
: URL of the Remark42 service, here I usedhttps://yu-remark42-01.fly.dev/
, which is automatically generated by fly.io. If you later have a custom domain, this will need to be changed.SITE
: Site name, here I usedremark42-demo
.SECRET
: Custom JWT Token, here I usedremark42-secret
.ADMIN_SHARED_ID
: Administrator ID, here I used an empty string, meaning no administrator, which can be supplemented later.
[[vm]]
: This section mainly contains machine-related configurations.cpu_kind
: CPU type, set toshared
.cpus
: Number of CPUs, set to 1.memory_mb
: Memory, set to 256MB.
Create Service#
After completing and checking the configuration, run the following command to create the service:
flyctl launch
Environment Variable Configuration#
Currently, only the service has been deployed, and environment variables have not been set, so the service startup will have issues. Next, we will set the environment variables in the prod.env
file:
AUTH_GITHUB_CID=<your_github_cid>
AUTH_GITHUB_CSEC=<your_github_csec>
AUTH_TWITTER_CID=<your_twitter_cid>
AUTH_TWITTER_CSEC=<your_twitter_csec>
AUTH_ANON=true
AUTH_TELEGRAM=true
TELEGRAM_TOKEN=<your_telegram_token>
NOTIFY_ADMINS=telegram
NOTIFY_TELEGRAM_CHAN=<your_telegram_group>
NOTIFY_USERS=email
AUTH_EMAIL_ENABLE=true
SMTP_HOST=smtp.gmail.com
SMTP_PORT=465
SMTP_TLS=true
[email protected]
SMTP_PASSWORD=<your_password>
[email protected]
[email protected]
The environment variable section is relatively complex; refer to the documentation for specific parameters.
Login/Authorization Configuration#
I configured anonymous comments, GitHub, Twitter, and Telegram login methods. You can configure other login methods based on your situation.
- Anonymous login
AUTH_ANON
: Whether to allow anonymous comments. I chose to allow it, meaning users can comment without logging in.
- GitHub login
AUTH_GITHUB_CID
andAUTH_GITHUB_CSEC
: Client ID and Client Secret of the GitHub OAuth App.
- Twitter login
AUTH_TWITTER_CID
andAUTH_TWITTER_CSEC
: Client ID and Client Secret of the Twitter OAuth App.
- Telegram login
AUTH_TELEGRAM
: Whether to allow Telegram login.TELEGRAM_TOKEN
: Telegram Bot Token created throughbotfather
.
- Email login
AUTH_EMAIL_ENABLE
: Whether to allow email login.AUTH_EMAIL_FROM
: Sending email for email login.
Notification Configuration#
- Telegram notifies administrators; refer to this part of the documentation for creating and configuring the Telegram Bot.
NOTIFY_ADMINS
: Method of notifying administrators, choose telegram.NOTIFY_TELEGRAM_CHAN
: If enabling telegram notification for administrators, you need to configure the corresponding Channel id. Just fill in the id part aftert.me/xxx
, such aspseudoyuchat
.
- Email notifies users; refer to this part of the documentation for configuring SMTP, etc.
NOTIFY_USERS
: Method of notifying users. I chose email, meaning email notifications, so the SMTP below needs to be configured.NOTIFY_EMAIL_FROM
: Sending address for email notifications.
Email SMTP Configuration#
The email login and email notification mentioned above require configuring the SMTP server. This part can also be configured according to your email service provider refer to the documentation.
SMTP_HOST
: SMTP server address.SMTP_PORT
: SMTP server port.SMTP_TLS
: Whether to enable TLS.SMTP_USERNAME
: SMTP username.SMTP_PASSWORD
: SMTP password.
Import Environment Variables into the Service#
After completing the environment variable configuration according to the above instructions, run the following command in the directory where the configuration file and environment variable file are located to import the environment variables:
fly secrets import < prod.env
After execution, check the service status in the fly.io console. If it shows Deployed
, it means the deployment was successful.
Configure Custom Domain (Optional)#
If you do not want to use the default domain provided by fly.io, you can configure a custom domain.
Enter the fly.io console, select the newly deployed yu-remark42-01
service, click on the Certificates
option on the left, and then click Add a Certificate
in the upper right corner to add a custom domain as prompted.
After clicking Create Certificate
, a page will display the DNS records you need to add. Follow the prompts to add them.
For example, my domain is hosted on Cloudflare. I added two DNS records as prompted. After returning to the page, click Check again
or wait a while and refresh to see if they all show green, indicating successful configuration.
At this point, we can modify REMARK_URL
in fly.toml
to the custom domain, then execute the following command to redeploy the service. After that, any changes to the configuration file can be updated using this command:
fly deploy
Blog Configuration for Remark42#
Having completed the deployment of the Remark42 service, we now need to add the Remark42 comment component to our blog posts. Taking my Hugo blog as an example.
Define the Comments Component in Hugo Theme#
I created a new file named comments.html
in the layouts/partials
directory of my Hugo blog to define the Remark42 comment component:
<div class="comments">
<div class="title">
<span>Comments</span>
<span class="counter"><span class="remark42__counter" data-url="{{ .Permalink }}"></span></span>
</div>
<div id="remark42">
</div>
</div>
<script>
var remark_config = {
host: 'https://comments.pseudoyu.com',
site_id: 'pseudoyu.com',
components: ['embed', 'counter'],
max_shown_comments: 20,
simple_view: true,
theme: 'light',
}
</script>
<script>
(function () {
// init or reset remark42
const remark42 = window.REMARK42
if (remark42) {
remark42.destroy()
remark42.createInstance(remark_config)
} else {
for (const component of remark_config.components) {
var d = document, s = d.createElement('script');
s.src = `${remark_config.host}/web/${component}.mjs`;
s.type = 'module';
s.defer = true;
// prevent the <script> from loading multiple times by InstantClick
s.setAttribute('data-no-instant', '')
d.head.appendChild(s);
}
}
})();
</script>
The host
and site_id
in remark_config
need to be modified according to your actual configuration, while other parts can remain unchanged or be adjusted according to the documentation.
After configuring the comments
component, include it at the bottom of the article in layouts/posts/single.html
:
{{ partial "comments.html" . }}
The general position is as shown in the image. If you are using other themes or blog systems, you need to find and modify the corresponding template file for your articles.
Local Preview/Deploy Website#
At this point, you can preview locally or deploy the website to check whether the comment system displays correctly. Thus, our service deployment is complete.
Obtain User ID and Configure Admin#
After completing the login authorization and testing comments, you can click on your avatar in Remark42 to open the management page. Double-clicking and pressing CMD/Ctrl+C
will allow you to obtain the User ID starting with github_
or other platforms. You can configure this in ADMIN_SHARED_ID
(modify the fly.toml
configuration file and run fly deploy
to redeploy, thus becoming an administrator, who has the authority to delete and manage other users' comments).
Others#
I exported the comment data from the previous Cusdis in JSON format according to certain conditions and used a Go program for format conversion and migration, thus retaining all previous comments.
Since Cusdis itself does not provide an export feature and the migration demand is too niche, I did not directly contribute code upstream or write a complete script. Friends with similar needs can refer to this PR for processing — "feat: add cusdis to remark42 migrator support by pseudoyu · Pull Request #1 · pseudoyu/remark42".
Conclusion#
This is the process of building my blog comment system. The construction and configuration of the comment system are relatively complex, and the configuration method in this article may become outdated over time. If you encounter problems, refer more to the official documentation.
This is one of my series of tutorials on blog building and deployment. If you are interested in building data statistics systems, internal blog search, etc., please stay tuned. I hope it can be helpful to everyone.