Artalk on Ubuntu 24.04 on Azure User Guide
Overview
Artalk is a lightweight, self hosted comment system: a privacy respecting alternative to hosted commenting services. A single self contained Go binary serves three things at once, an embeddable comment widget you drop into any page, a REST API for the comments themselves, and a full administration sidebar for moderating threads and managing sites. Comments live in an embedded SQLite database, so there is no separate database server to run. The cloudimg image runs Artalk as a dedicated non root service user bound to loopback, with nginx as the public listener on port 80, and generates a unique administrator login on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- Artalk 2.10.0 as a single self contained Go binary at
/usr/local/bin/artalk, with the admin sidebar and the comment widget compiled into it - nginx on
:80as the sole public listener, reverse proxying to Artalk on127.0.0.1:23366 - An embedded SQLite database at
/var/lib/artalk/data/artalk.db, owned by theartalkservice user and not world readable - A per VM administrator account and JWT signing key generated at first boot, written to a root only file. The image ships with no database at all, so it contains no account to sign in as
artalk.service,artalk-firstboot.serviceandnginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Key facts:
| Item | Value |
|---|---|
| Default SSH user | azureuser |
| Public port | 80/tcp (nginx) |
| Application port | 127.0.0.1:23366 (loopback only) |
| Admin sidebar | http://<vm-public-ip>/sidebar/ |
| Configuration | /etc/artalk/artalk.yml |
| Data directory | /var/lib/artalk |
| Credentials file | /root/artalk-credentials.txt |
Prerequisites
- An active Azure subscription with permission to create virtual machines.
- An SSH key pair for the
azureuseraccount. - A virtual network and subnet in your target region.
- A network security group that allows
22/tcpfrom your management network and80/tcp(plus443/tcponce you enable HTTPS) from the networks that will load your comment widget and reach the admin sidebar.
Recommended VM size: Standard_B2s (2 vCPU / 4 GiB RAM). Artalk is a single Go process with an embedded database and is comfortable at this size for a typical blog or documentation site.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Artalk by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create and Create.
First boot initialisation, which generates the JWT signing key, resolves this VM's site URL and creates the administrator account, completes within about a minute of the VM starting. The public listener stays closed until it has finished, so a connection refused in the first minute simply means bootstrap is still running.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name artalk \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name artalk --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
If you need the address, az vm show --show-details --resource-group <your-rg> --name artalk --query publicIps -o tsv prints it.
Step 4 - Confirm the services are running
systemctl is-active artalk.service nginx.service
artalk --version
curl -s -o /dev/null -w 'instance config: HTTP %{http_code}\n' http://127.0.0.1/api/v2/conf
curl -s -o /dev/null -w 'admin sidebar: HTTP %{http_code}\n' -L http://127.0.0.1/sidebar/
Both units report active, artalk --version prints Artalk (v2.10.0), and both HTTP checks return 200. That confirms Artalk is serving and nginx is proxying to it.

Step 5 - Retrieve your administrator login
Every VM generates its own administrator password on first boot and writes it to a root only file. Nothing is shared between deployments.
sudo cat /root/artalk-credentials.txt
sudo stat -c '%a %U:%G %n' /root/artalk-credentials.txt
The file lists the sidebar URL, the administrator name and email, and the password. It is mode 600 and owned by root, so only a user with sudo can read it.

Step 6 - Sign in to the admin sidebar
Open http://<vm-public-ip>/sidebar/ in a browser. Enter the artalk.admin.email and artalk.admin.pass values from Step 5 and select Login.

Step 7 - Moderate comments
The Comment view is where every comment across every site you host lands. All, Pending and Personal tabs filter the queue, and each comment carries inline Reply, Collapse, Approve, Pin, Edit and Delete actions. Replies render nested under the comment they answer, so a thread reads the same way here as it does on your page.

Step 8 - Review your site
The Site view lists every site this instance serves comments for. The image creates one site called My Site on first boot and sets its URL to this VM's own address, which Artalk uses as the CORS allow list entry for the comment widget. Use the + button to add another site, and select a site to edit its name or URL.

When you point a real domain at this VM, update the site URL here and add the same origin to trusted_domains in /etc/artalk/artalk.yml, then restart the service:
sudo sed -i 's|^trusted_domains: .*|trusted_domains: ["https://<your-domain>"]|' /etc/artalk/artalk.yml
sudo systemctl restart artalk.service
Step 9 - Embed the comment widget on your site
Add these three lines to any page you want comments on. server is your Artalk VM, site matches the site name from Step 8, and pageKey identifies the page the thread belongs to.
<link href="http://<vm-public-ip>/dist/Artalk.css" rel="stylesheet" />
<script src="http://<vm-public-ip>/dist/Artalk.js"></script>
<div id="artalk"></div>
<script>
Artalk.init({
el: '#artalk',
server: 'http://<vm-public-ip>',
site: 'My Site',
pageKey: '/blog/hello-artalk/',
pageTitle: 'Hello Artalk'
})
</script>
Both widget assets are served straight from the Artalk binary, so there is no CDN to depend on and no build step:
curl -s -o /dev/null -w 'Artalk.js: HTTP %{http_code} %{content_type}\n' http://127.0.0.1/dist/Artalk.js
curl -s -o /dev/null -w 'Artalk.css: HTTP %{http_code} %{content_type}\n' http://127.0.0.1/dist/Artalk.css
Readers get a comment box with a Markdown editor, live preview, emoticons and image attachment, and the thread renders underneath with nested replies and voting.

Step 10 - Prove the administrator login through the API
Artalk exposes the same REST API the sidebar uses. This exchanges your per VM credentials for a JWT and then calls an administrator only endpoint with it.
TOKEN=$(curl -s -H 'Content-Type: application/json' \
-d '{"name":"<ARTALK_ADMIN_NAME>","email":"<ARTALK_ADMIN_EMAIL>","password":"<ARTALK_ADMIN_PASSWORD>"}' \
http://127.0.0.1/api/v2/user/access_token | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
echo "token length: ${#TOKEN}"
curl -s -o /dev/null -w 'admin sites API with token: HTTP %{http_code}\n' \
-H "Authorization: Bearer $TOKEN" http://127.0.0.1/api/v2/sites
curl -s -o /dev/null -w 'admin sites API anonymously: HTTP %{http_code}\n' \
http://127.0.0.1/api/v2/sites 2>/dev/null
The token is roughly 144 characters, the authenticated call returns 200, and the anonymous call does not. Use the same bearer token for any other administrator endpoint; the full API is documented at http://<vm-public-ip>/swagger/index.html.
Step 11 - Confirm the runtime and security posture
ss -tln | grep -E ':(80|23366) '
ls -l /var/lib/artalk/data/artalk.db
sudo apt-mark showhold
echo "pending package upgrades: $(sudo apt-get -s -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade 2>/dev/null | grep -c '^Inst ')"
grep -E '^(site_url|trusted_domains|admin_users):' /etc/artalk/artalk.yml
Artalk listens on 127.0.0.1:23366 only, so the application is reachable exclusively through nginx on :80. The SQLite database is mode 640 owned by artalk, because it holds bcrypt password hashes. There are no held packages and no pending upgrades. site_url and trusted_domains carry this VM's own address rather than a baked in placeholder, and admin_users is empty because the administrator lives in the database as a bcrypt hash, not in the configuration file as plain text.

Managing the service
systemctl status artalk.service --no-pager | head -n 6
Standard systemd verbs apply. Restart Artalk after any configuration change:
sudo systemctl restart artalk.service
sudo journalctl -u artalk.service -f
Application logs are also written to /var/lib/artalk/data/artalk.log.
Configuration reference
/etc/artalk/artalk.yml is the single configuration file. The full upstream reference, with every option annotated, ships at /usr/share/doc/artalk/artalk.reference.yml. Values worth knowing:
| Key | Shipped value | Notes |
|---|---|---|
host / port |
127.0.0.1 / 23366 |
Loopback only. nginx is the public listener |
app_key |
per VM 64 hex | JWT signing key generated at first boot |
site_default / site_url |
My Site / this VM |
The default site and its URL |
trusted_domains |
this VM | CORS allow list for sites embedding the widget |
db.type / db.file |
sqlite / ./data/artalk.db |
Relative to /var/lib/artalk |
captcha.enabled |
true |
Image captcha after action_limit actions |
img_upload.enabled |
true |
Image attachments, 5 MB cap |
email.enabled |
false |
Set your own SMTP relay to enable notifications |
auth.enabled |
false |
Social sign in providers, all disabled with empty secrets |
Enabling HTTPS
Comment widgets are loaded by browsers on your own pages, so serving Artalk over HTTPS is strongly recommended. Point a DNS record at the VM, open 443/tcp in the network security group, then:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>
Certbot rewrites the nginx vhost at /etc/nginx/sites-available/artalk in place, keeping the proxy buffer settings. Afterwards update site_url and trusted_domains in /etc/artalk/artalk.yml to the https:// origin and restart Artalk, then change the server value in your embed snippet to match.
Backup and maintenance
Everything that matters is the SQLite database plus the configuration file:
sudo systemctl stop artalk.service
sudo tar czf artalk-backup-$(date +%F).tar.gz /var/lib/artalk/data /etc/artalk/artalk.yml
sudo systemctl start artalk.service
Artalk also has a built in exporter under Transfer in the sidebar, which produces a JSON dump you can re import into another instance.
Operating system security updates are applied automatically by unattended-upgrades. To move to a newer Artalk release, replace /usr/local/bin/artalk with the new release binary and restart the service; the database schema migrates automatically on start.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
Connection refused on :80 in the first minute |
First boot bootstrap has not finished. artalk.service and nginx.service deliberately do not start until artalk-firstboot.service has created the administrator. Check with systemctl status artalk-firstboot.service |
HTTP 403 with need_captcha when signing in |
Artalk's action limiter allows captcha.action_limit (3) actions per client IP inside captcha.action_reset (60 s), then requires a captcha. This is the built in abuse control for anonymous commenting. Wait a minute, or complete the captcha the sidebar shows |
HTTP 401 signing in |
Wrong password. Re read /root/artalk-credentials.txt; the password is unique to this VM |
| Comments do not appear on your own page | The page origin is not in the site URL or trusted_domains, so the browser blocks the cross origin request. Add it in the Site view and in /etc/artalk/artalk.yml, then restart |
502 Bad Gateway from nginx |
Artalk is not running. sudo journalctl -u artalk.service -n 50 --no-pager |
| Signed out after a restart | Only happens if app_key was blanked. It is set per VM at first boot; check grep '^app_key:' /etc/artalk/artalk.yml is not empty |
Useful diagnostics:
systemctl is-active artalk-firstboot.service
sudo journalctl -u artalk-firstboot.service --no-pager | tail -n 12
Security recommendations
- Restrict SSH. Limit
22/tcpin the network security group to your management network. - Enable HTTPS before publishing the widget, so reader comments are not sent in clear text.
- Keep the credentials file safe.
/root/artalk-credentials.txtis mode600; change the administrator password from the sidebar and delete the file once you have stored it in your password manager. - Leave the captcha enabled.
captcha.enabledis Artalk's abuse control for anonymous commenting and image upload. If you prefer a hosted challenge, switchcaptcha.captcha_typetoturnstile,recaptchaorhcaptchaand add the corresponding keys. - Review image upload.
img_upload.enabledis on with a 5 MB cap so readers can attach images. Set it tofalseif you do not want anonymous uploads. - Moderate before publishing by setting
moderator.pending_default: true, which holds every new comment for approval.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription ID and the VM name. For questions about Artalk itself, the upstream documentation is at artalk.js.org.