A2
Applications AWS

Artalk 2.10.1 on AWS User Guide

| Product: Artalk 2.10.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Artalk 2.10.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg forums and social networks listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Artalk is a self hosted comment system, a privacy friendly alternative to Disqus. You add a small script to your site's page template and every page gets its own comment thread: readers comment, reply, vote and format with Markdown, and you moderate everything from Artalk's admin sidebar, for one site or many, including importing comments from Disqus, WordPress and other systems. Artalk is one Go program with its database (SQLite), its comment widget and its admin sidebar built in.

A blog post page with the Artalk comment widget served by the instance: the comment box with a reader named Sam typing a question, the count of 4 comments, comments from Mei Lin with inline code and from Tomasz with bold Markdown, and Priya's question with a threaded reply from the verified admin

The cloudimg image makes it safe to run on EC2:

  • No default account and no baked secret. The image ships with no administrator, no app_key (the key Artalk signs every sign-in token with), no database and no TLS key. On your instance's first boot a 64 character app_key and a 24 character password for the site-wide administrator admin are generated. The configuration file stores only a bcrypt hash of the password; the password itself is in a root only file. There is no install wizard.
  • No secret in a process environment or on a command line. Artalk reads its settings from /etc/artalk/artalk.yml (readable by root and the artalk service only); nothing secret is passed to it in an environment variable or an argument.
  • The settings cannot be changed from the web. The configuration file is read-only to Artalk, so the sidebar's Settings page cannot save. You change settings as root on the instance (Step 9), which keeps features such as upload commands out of reach of a stolen administrator session.
  • Only your sites can embed it. Artalk answers cross-origin requests (CORS) and allows framing of its sidebar (frame-ancestors) only for this instance and the sites you add with artalk-sites, never for *. Any other site's page that tries to post or vote with a reader's browser is refused.
  • The administrator plane refuses other sites. nginx refuses a request to the administrator API (settings, sites, users, import and export) that another web site's page sends from your browser, even with a valid sign-in.
  • Sign-in is rate limited on the real client address. Artalk asks for a captcha after three sign-ins or comments in a minute from one address, and nginx answers 429 to more than about ten sign-ins a minute from one address. nginx replaces any forwarded address header a client sends, so a client cannot dodge either limit by inventing addresses.
  • No outbound access inside. The artalk account is refused, at the kernel level and on every boot, any connection to the EC2 instance metadata service, link-local, private (RFC 1918), carrier grade NAT and reserved addresses and your VPC's own address ranges, and all IPv6. You can allow one private address at a time, for example your SMTP relay (Step 10). Every outbound feature (email, notifications, captcha services, social sign-in) is off with empty credentials.
  • HTTPS from the first boot. nginx serves Artalk on port 443 with a certificate generated on your instance's first boot; port 80 redirects to HTTPS. Nothing answers until first boot has finished.
  • Artalk listens on localhost only (127.0.0.1:23366) and runs as its own unprivileged user with no Linux capabilities.
  • Built from source, with current fixes. Artalk 2.10.1 is built from its tagged release source with Go 1.26.10, with four of its Go libraries raised to their fixed releases; the sidebar and widget are upstream's own 2.10.1 build, checked file by file.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Artalk 2.10.1 (/opt/artalk/artalk, configuration in /etc/artalk/artalk.yml, the database, uploaded images and Artalk's log in /var/lib/artalk/data), nginx, the tools artalk-sites, artalk-admin-password, artalk-set-hostname and artalk-selftest, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with internet access.
  • A security group that allows TCP 22 (SSH) from your own network, and TCP 443 (Artalk) and TCP 80 (redirects to HTTPS) from your readers, which for a public website means from anywhere.
  • A website whose page template you can edit, to embed the comment widget.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Artalk 2.10.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (Artalk itself needs very little, so a smaller instance such as t3.small serves a busy blog), your key pair, a subnet and a security group as above.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=artalk}]'

Step 2: Connect to your instance

Version SSH user
Artalk 2.10.1 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

First boot sets the host name, generates the certificate, the app_key and the administrator password; the egress backstop loads; then Artalk and nginx start:

systemctl is-active artalk-firstboot cloudimg-artalk-egress artalk nginx
sudo test -f /var/lib/cloudimg/artalk-firstboot.done && echo FIRSTBOOT_DONE

All four units are active and the first boot marker is present:

active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx listen on the network. Artalk listens on localhost only:

sudo ss -lntpH | awk '{print $4, $6}' | sed -E 's/users:\(\("([^"]+)".*/\1/' | grep -v '127.0.0.5' | sort -u
0.0.0.0:22 sshd
0.0.0.0:443 nginx
0.0.0.0:80 nginx
127.0.0.1:23366 artalk
[::]:22 sshd
[::]:443 nginx
[::]:80 nginx

Step 4: Get your URL and administrator password

sudo cat /var/lib/cloudimg/artalk-credentials.txt

The file is readable by root only and holds the URLs, the default site's name and the administrator's user name, email and password:

# Artalk 2.10.1 - generated on first boot by artalk-firstboot.service
# The administrator password is unique to this instance; none was baked into the image. Keep this file secret.

ARTALK_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
ARTALK_SIDEBAR_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/sidebar/
ARTALK_SITE=My Site
ARTALK_ADMIN_USER=admin
ARTALK_ADMIN_EMAIL=admin@artalk.local
ARTALK_ADMIN_PASSWORD=<ARTALK_ADMIN_PASSWORD>
#
# Sign in to the sidebar (moderation, sites, settings, import/export) with the user name and email above and this
# password; 'admin' is a site-wide superuser. Readers comment through the widget you embed in your site; no reader
# account or social login is configured.
# Allow your site to embed the widget:  sudo artalk-sites add https://your-blog.example
# New administrator password:           sudo artalk-admin-password
# Self-test:  sudo artalk-selftest      Change the served name:  sudo artalk-set-hostname <your-domain>
# Settings: edit /etc/artalk/artalk.yml as root, then sudo systemctl restart artalk (the sidebar cannot save them).
# User guide: https://www.cloudimg.co.uk/guides/artalk-2-10-1-on-ubuntu-24-04-aws/

The URL uses the instance's public DNS name from EC2 instance metadata. Opening https://<public-ip>/ sends you on to that name.

The image includes a self test. Through nginx it checks the redirect, the health and version endpoints, the sidebar and the widget script, the content security policy, that the administrator signs in with the password and a wrong password is refused, that the administrator API refuses no sign-in and another site and accepts the administrator, the cross-origin and unknown host guards, the service's privileges, the egress backstop and the metadata block:

sudo artalk-selftest
SELFTEST-OK https 301 + healthz + version 2.10.1 (53d15ee) + sidebar + widget script + CSP frame-ancestors without * and no X-Frame-Options + administrator sign-in 200, wrong password 401 + admin API 403 anonymous, 200 administrator, 403 cross-site + 403 cross-origin POST + 444 unknown host + CORS only for trusted origins + artalk uid artalk, no capabilities, NoNewPrivs, listening 127.0.0.1:23366 only, egress backstop loaded + metadata 000 inside artalk and nginx (control 200)

Step 5: Sign in to the admin sidebar

Open ARTALK_SIDEBAR_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (Step 11 replaces it). Enter ARTALK_ADMIN_EMAIL and ARTALK_ADMIN_PASSWORD and choose Login:

The Artalk admin sidebar sign in page with the Artalk logo, the Email box holding admin@artalk.local, an empty Password box and the Login button, with Powered by Artalk v2.10.1 / 53d15ee at the foot

The Comment view lists every comment on every site this instance serves. All, Pending and Personal filter it, and each comment has Reply, Collapse, Approved, Pin, Edit and Delete actions. The administrator's own comments carry a verified badge. Page lists the pages that have comments, User the readers, Site your sites and Transfer imports and exports comments:

The Artalk sidebar Comment view, signed in as admin: the Comment, Page, User, Site, Transfer and Settings menu on the left, the All, Pending and Personal tabs, and comments from Mei Lin, Tomasz, the verified admin replying to Priya, and Priya, each with Up, Reply, Collapse, Approved, Pin, Edit and Delete actions

Artalk asks for a captcha after three sign-ins or comments in one minute from the same address; wait a minute or answer the captcha.

Step 6: Allow your website

This is the step that makes Artalk work on your own site. The comment widget on your page calls Artalk's API from your page's origin, and Artalk answers only the origins it trusts. A new instance trusts only itself, so list prints nothing:

sudo artalk-sites list

Add each site that will embed comments, as an exact origin: https:// plus the host name, and a port if it is not 443. artalk-sites writes the list into Artalk's trusted_domains (its CORS answer), into nginx's frame-ancestors policy and request guards, and restarts Artalk:

sudo artalk-sites add https://blog.example.com
sudo artalk-sites remove https://blog.example.com

A wildcard (* or https://*.example.com), an http:// origin, localhost and a URL with a path are refused. Adding a site only in the sidebar's Site view is not enough, because nginx's guards do not see it. The trade-off: each listed site can call the API with a reader's browser from its own pages, so list only sites you control, and add each host name your blog answers on (for example both https://example.com and https://www.example.com).

Step 7: Embed the widget in your site

Add this to your page template where the comments should appear, with the host from ARTALK_URL. site is the site name (the image creates My Site; you can rename it or add more sites in the sidebar's Site view), and pageKey identifies the page the thread belongs to:

<link href="https://comments.example.com/dist/Artalk.css" rel="stylesheet">
<script src="https://comments.example.com/dist/Artalk.js"></script>
<div id="artalk"></div>
<script>
  Artalk.init({
    el: '#artalk',
    server: 'https://comments.example.com',
    site: 'My Site',
    pageKey: '/blog/hello-artalk/',
    pageTitle: 'Hello Artalk'
  })
</script>

Both widget files are served by the Artalk program itself, so there is no CDN to depend on:

H=$(sudo sed -n 's#^ARTALK_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/artalk-credentials.txt)
curl -sk -o /dev/null -w 'Artalk.js:  %{http_code} %{content_type}\n' --resolve "$H:443:127.0.0.1" "https://$H/dist/Artalk.js"
curl -sk -o /dev/null -w 'Artalk.css: %{http_code} %{content_type}\n' --resolve "$H:443:127.0.0.1" "https://$H/dist/Artalk.css"
Artalk.js:  200 text/javascript
Artalk.css: 200 text/css

Readers get a comment box with a Markdown editor, preview, emoticons and image attachments, and the thread underneath with nested replies and voting, as in the picture at the top of this guide. A reader names themselves with a nickname and an email address (shown to you, never to other readers). Readers' browsers will refuse the self signed certificate, so install a trusted one (Step 11) before you go live. The Artalk documentation lists every widget option.

Step 8: The administrator

admin is a site-wide superuser: it moderates every site, manages sites and users and imports and exports comments. Its password hash is in admin_users in /etc/artalk/artalk.yml, which Artalk applies at every start, so a password change in the sidebar's user editor does not last. Change the password with the helper instead. It generates a new 24 character password, writes its hash into the configuration and the password into the credentials file, and restarts Artalk; signed-in sessions stay valid until they expire:

sudo artalk-admin-password
sudo cat /var/lib/cloudimg/artalk-credentials.txt

The administrator can also use Artalk's REST API, the same one the sidebar uses: POST /api/v2/user/access_token with the name, email and password returns a token, sent as Authorization: Bearer <token>. Keep tokens out of command lines and shell history.

Step 9: Settings, notifications and sign-in options

The sidebar's Settings page shows the configuration but cannot save it: /etc/artalk/artalk.yml is read-only to Artalk. Edit it as root and restart Artalk:

sudo nano /etc/artalk/artalk.yml
sudo systemctl restart artalk

The image ships with these defaults:

Setting Shipped value Notes
captcha.enabled true (image captcha) A captcha after action_limit (3) actions in action_reset (60) seconds from one address
img_upload.enabled true, max_size: 5 (MB) Readers can attach images, stored in /var/lib/artalk/data/artalk-img
moderator.pending_default false Set true to hold every new comment for approval
email.enabled false Email notifications; add your own SMTP relay
auth.enabled false Social and email sign-in for readers, all providers off with empty secrets

app_key, admin_users, site_url and trusted_domains are managed by first boot and the helpers; change them only with the tools named at the top of the file. The full annotated upstream reference is /usr/share/doc/cloudimg-artalk/artalk.reference.yml.

To send reply notifications by email, set email.enabled: true, send_type: smtp, the sender send_addr and your relay's host, port, username and password under email.smtp. A relay on the internet works straight away; a relay on a private address in your VPC must also be allowed in the egress list (Step 10). The same applies to the notifiers, a hosted captcha (Turnstile, reCAPTCHA, hCaptcha, Geetest) and social sign-in providers, which need your own keys.

Step 10: Outbound connections

Artalk makes outbound connections only for the features you switch on: email, notifications, hosted captchas, social sign-in and fetching a page's title. Because some of those addresses come from settings and readers, the artalk account is refused any connection to the instance metadata service, link-local, private, carrier grade NAT, benchmarking (198.18.0.0/15), IETF protocol (192.0.0.0/24), multicast and reserved addresses and this VPC's own IPv4 ranges (read from instance metadata on every boot), and every IPv6 connection. If the VPC ranges cannot be read, every new IPv4 connection of the artalk account is refused until they can. Artalk does not start without these rules:

sudo iptables -S CLOUDIMG-ARTALK-EGRESS | grep -c -- '-j REJECT'
sudo ip6tables -S CLOUDIMG-ARTALK-EGRESS | tail -n 1
sudo cat /run/cloudimg/artalk-vpc-cidrs
24
-A CLOUDIMG-ARTALK-EGRESS -j REJECT --reject-with icmp6-adm-prohibited
172.31.0.0/16

The count of refused IPv4 ranges includes your VPC's ranges, and the VPC range shown is your own. To let Artalk reach a service on a private address (an SMTP relay or webhook receiver in your VPC), add its range, one CIDR per line, to /etc/cloudimg/artalk-egress-allow and reload; metadata and link-local addresses can never be allowed:

echo 10.20.30.40/32 | sudo tee -a /etc/cloudimg/artalk-egress-allow
sudo systemctl reload cloudimg-artalk-egress

Step 11: Use your own domain and certificate

  1. In your DNS, point <your-domain> (an A record, for example comments.example.com) at the instance's public IP address. An Elastic IP keeps that address stable.
  2. Tell the instance to use it. This sets the name nginx accepts, the request guards and Artalk's site_url, and generates a new self signed certificate for it:
sudo artalk-set-hostname <your-domain>
  1. For a certificate your readers' browsers trust, put the certificate chain for <your-domain> in /etc/ssl/artalk/artalk.crt and its private key in /etc/ssl/artalk/artalk.key (root only, mode 600), and run sudo systemctl reload nginx. Alternatively put the instance behind an Application Load Balancer with an AWS Certificate Manager certificate that forwards to port 443 on the instance.

Comments are stored against the site name and the page key, so changing the comment server's name keeps every thread. Change server in your embed code to match.

Step 12: Data and backups

Everything Artalk keeps is in /var/lib/artalk/data: artalk.db (the SQLite database), artalk-img/ (images readers attach) and artalk.log. All of it is private to the artalk account:

sudo ls -l /var/lib/artalk/data | awk 'NR>1{print $1, $3, $NF}'
drwx------ artalk artalk-img
-rw------- artalk artalk.db
-rw------- artalk artalk.log

To take a consistent copy, stop Artalk briefly:

sudo systemctl stop artalk
sudo tar czf /root/artalk-backup-$(date +%F).tar.gz /var/lib/artalk/data /etc/artalk/artalk.yml
sudo systemctl start artalk

The sidebar's Transfer view exports all comments as an Artrans JSON file and imports one. To bring comments over from Disqus, WordPress, Typecho, Valine, Waline, Twikoo and other systems, convert their export to Artrans with Artalk's Artransfer tool first, then import it. An EBS snapshot of the instance's volume is also a complete backup. The backup holds app_key and the administrator's password hash, so keep it as safe as the instance.

Step 13: Security checks

nginx and Artalk are blocked from the instance metadata service:

for u in nginx artalk; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | tr '\n' ' '; echo; done
nginx 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 
artalk 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 

Artalk runs as artalk with no Linux capabilities and cannot gain any:

P=$(systemctl show -p MainPID --value artalk)
ps -o user= -p "$P"
grep -E '^(CapEff|CapBnd|CapAmb|NoNewPrivs)' /proc/"$P"/status
artalk
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000
NoNewPrivs: 1

The administrator API refuses a request without a sign-in and one that another site's page sends; a comment from a site you have not listed is refused and gets no CORS answer; a request for a host name the instance does not serve gets no answer at all; and plain HTTP redirects:

H=$(sudo sed -n 's#^ARTALK_URL=https://\([^/]*\)/$#\1#p' /var/lib/cloudimg/artalk-credentials.txt)
curl -sk -o /dev/null -w 'admin API without a token: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: same-origin' "https://$H/api/v2/sites"
curl -sk -o /dev/null -w 'admin API from another site: %{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Sec-Fetch-Site: cross-site' "https://$H/api/v2/settings"
curl -sk -o /dev/null -w 'comment from an unlisted site: %{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Content-Type: application/json' -H 'Origin: https://evil.example' --data '{}' "https://$H/api/v2/comments"
curl -sk -D - -o /dev/null --resolve "$H:443:127.0.0.1" -H 'Origin: https://evil.example' "https://$H/api/v2/conf" | grep -ci '^access-control-allow-origin' | sed 's/^/CORS headers for an unlisted site: /'
curl -sk -o /dev/null -w 'unknown host name: %{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true
curl -s -o /dev/null -w 'http: %{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/sidebar/"
admin API without a token: 403
admin API from another site: 403
comment from an unlisted site: 403
CORS headers for an unlisted site: 0
unknown host name: 000
http: 301

The credentials, the configuration, the TLS key and the database are readable by root (and the configuration and database by the artalk service user) only, and Artalk's process environment carries no secret:

P=$(systemctl show -p MainPID --value artalk)
sudo stat -c '%a %U:%G %n' /var/lib/cloudimg/artalk-credentials.txt /etc/artalk/artalk.yml /etc/ssl/artalk/artalk.key /var/lib/artalk /var/lib/artalk/data/artalk.db /proc/"$P"/environ
600 root:root /var/lib/cloudimg/artalk-credentials.txt
640 root:artalk /etc/artalk/artalk.yml
600 root:root /etc/ssl/artalk/artalk.key
700 artalk:artalk /var/lib/artalk
600 artalk:artalk /var/lib/artalk/data/artalk.db
400 artalk:artalk /proc/<pid>/environ

Other notes:

  • Rate limits. Artalk's own limiter asks for a captcha after three sign-ins or comments a minute from one address (the administrator's signed-in requests are exempt). nginx also allows about ten sign-in requests a minute from one address, with a small burst, and answers 429 beyond that.
  • Client addresses. nginx replaces any X-Forwarded-For or X-Real-IP a client sends with the address it connected from, and only nginx can reach Artalk's port, so the limits, the comment record and Artalk's log always see the real address. If you put a load balancer in front, every reader appears to come from it; keep the limits in mind, or terminate TLS on the instance.
  • NVMe host identity. No NVMe-oF host identity ships in the image. If you install nvme-cli, its host NQN and host ID are your instance's own.
  • Who can reach it. Keep port 22 open only to your own network. Ports 80 and 443 serve your readers.
  • The build. /usr/share/doc/cloudimg-artalk/BUILD-INFO.txt records the release, the Go toolchain and the dependency updates; go.mod, go.sum and go-version-m.txt next to it list every library in the program, and ui-manifest.txt the sidebar and widget files.
  • Logs. nginx logs are in /var/log/nginx (without query strings); Artalk logs to the journal (journalctl -u artalk) and to /var/lib/artalk/data/artalk.log.

Step 14: Keep it up to date

Ubuntu security updates install automatically. To apply all updates now:

sudo apt-get update && sudo apt-get upgrade -y

New Artalk releases arrive as new versions of this listing, built and checked the same way. Do not run artalk upgrade: it would replace the checked program with a download that has none of this image's dependency fixes. To move to a new version, take a backup or a Transfer export (Step 12), launch the new version, add your sites and restore or import it. Read the Artalk release notes before you move.

Support and licensing

Artalk is open source under the MIT licence (the Artalk authors); the licence text is in /usr/share/doc/cloudimg-artalk/Artalk-LICENSE, and the source code is at github.com/ArtalkJS/Artalk. The Go and web libraries it is built with are open source, mainly under the MIT, Apache 2.0 and BSD licences; go-version-m.txt in the same directory lists them. There is no per user licence fee. The AMI price covers cloudimg's build, hardening, first boot setup and support.

  • Email: support@cloudimg.co.uk
  • Website: https://www.cloudimg.co.uk
  • Support: 24/7, with a 24 hour response SLA

The other versions on this listing are on the forums and social networks guide.