Isso
Overview
This image runs Isso 0.14.0, the open-source, self-hosted commenting server by Martin Zimmermann, on Ubuntu 24.04 LTS. Isso gives any page on your website a comment thread that you own. You drop a single script tag into your template, and readers get threaded discussions, replies, Markdown, voting and email notifications, while every comment lives in a database on your own VM.
What you get:
- A deliberately small stack. The server is a Python WSGI application running under gunicorn bound to
127.0.0.1:8080, behind nginx on port 80. The entire data tier is one SQLite file. There is no separate database server, no cache tier, and no third-party service holding your readers' conversations or profiling them for advertising. The application has no public listener of its own, so nginx is the only front door. - Secure by default. Isso's own shipped configuration contains a published default admin password (
please_choose_a_strong_password). This image never ships it. On the first boot of every VM,isso-firstboot.servicegenerates two values unique to that VM: a 32-character admin password, which is the only credential for the moderation interface, and a per-VM email hash salt, so that commenter email hashes cannot be recovered with a rainbow table built against the salt every other Isso install shares. - An empty password that authenticates nobody. The image is captured with the admin password empty. Isso's login only proceeds
if data["password"] and data["password"] == password, so an empty submitted field is rejected before comparison and an empty configured password can never be matched. A fixed default value would be worse, because a default published in a public image is itself a known credential. - Guards that refuse to start. Isso will refuse to start until first boot has generated real values, on two independent levers.
isso.servicedeclares a hardRequiresdependency on the rotation. A preflight check re-runs on every start and refuses an empty, default or short password, which also catches a hand-edited configuration and not only a failed first boot. - No database in the image. This is a security control rather than housekeeping. Isso signs its admin session cookie with a
session-keythat it generates withos.urandomand stores inside the SQLite database, once, when the database is first created. An image that shipped a database would give every customer VM the same signing key, and anyone could forge an administrator session against any VM built from it. Because the image carries no database, Isso mints a fresh signing key on your VM's first start.
Step 1 - Launch the VM
Launch from the Azure Marketplace listing, or from the Azure CLI. Isso is small, so Standard_B2s is the recommended size and is ample for a busy site.
To use the CLI, run these commands:
az group create --name isso-rg --location eastus
az vm image terms accept --urn sac1647283583153:isso-ubuntu-24-04:default:latest
az vm create \
--resource-group isso-rg \
--name isso-vm \
--image sac1647283583153:isso-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# Open HTTP so readers can reach the comment server. Never open 8080: gunicorn is bound to
# loopback and nginx is the only intended entry point.
az vm open-port --resource-group isso-rg --name isso-vm --port 80 --priority 900Note the public IP address that az vm create prints. This guide uses it as <vm-ip> throughout.
Step 2 - Confirm the services are running
Connect over SSH as azureuser and confirm that all three units are active. isso-firstboot is a oneshot unit, so active here means it ran to completion and rotated this VM's secrets.
systemctl is-active isso nginx isso-firstbootThe expected output is:
active active activeConfirm that Isso itself is bound only to loopback, and that nginx is the only public listener:
sudo ss -tlnp | grep -E ':80 |:8080 'The expected state is 127.0.0.1:8080 for the Python application and 0.0.0.0:80 for nginx. If gunicorn were listening on 0.0.0.0:8080, the application would be reachable around nginx.
Step 3 - Retrieve this VM's admin password
The admin password was generated on this VM's first boot and written to a root-owned file with mode 0600. Confirm that the file exists:
sudo ls -l /root/isso-credentials.txtThen read it:
sudo cat /root/isso-credentials.txtThe file contains the site URL, the admin URL and ISSO_ADMIN_PASSWORD. There is no username. Isso's admin interface is authenticated by the password alone.
Store the password in your password manager. Nothing else on the VM will show it to you again in plain text, and the value is unique to this VM.
Step 4 - Verify the server is answering
Isso exposes its effective configuration at /config. This is the appliance's health endpoint:
curl -s http://<vm-ip>/configNow confirm the endpoint that the comment widget itself uses. Isso's comment-fetch endpoint is the site root, with a uri query parameter naming the page whose thread you want. This is the exact request your readers' browsers make:
curl -s 'http://<vm-ip>/?uri=%2F' | head -c 200The response is JSON with a replies array, not HTML. Finally, prove that this VM's password authenticates. A 302 is a successful sign-in. Isso re-serves the login form with 200 when a password is wrong:
curl -s -o /dev/null -w "%{http_code}\n" -X POST -F "password=<isso-admin-password>" http://<vm-ip>/login/Step 5 - Add your website to the whitelist
This is the step that makes Isso work on your own site, and skipping it is the most common reason a freshly deployed widget stays empty.
Isso only accepts comments from sites you explicitly list. This is a deliberate security boundary. It stops anyone else embedding your comment server on their pages. On first boot the image whitelists this VM's own addresses so that the built-in demo works immediately, but your site is not in that list yet.
Open the configuration:
sudo nano /etc/isso/isso.cfgFind [general] host and add your site's URL, one per line, indented. Keep the existing entries. List a non-SSL URL first if you serve both, because some browsers suppress the referrer on plain HTTP requests:
[general] host = http://<vm-ip>/ https://blog.example.com/ http://blog.example.com/Restart Isso, then confirm that it connected to a configured site. Isso HEAD-checks each host at start-up, so give it a few seconds before reading the log:
sudo systemctl restart isso sleep 10
sudo journalctl -u isso -o cat -n 20 | grep -iE "connected to|unable to connect"connected to ... is the healthy state. unable to connect to your website means Isso could not reach any URL in the list from the VM. Check DNS, and check that the site is reachable from this network.
Why one of the listed URLs is this VM's private address. An Azure VM cannot reach its own public IP, because Azure's Standard Load Balancer does not route a VM's traffic back to its own frontend address. Isso HEAD-checks each configured host at start-up and warns if none answer. The image therefore lists the private address as well, which the VM can always reach. It also lets you serve readers over the VNet without a public IP at all.
Step 6 - Embed the widget in your site
Add these two lines to your page template, wherever you want the thread to appear. This is the whole integration.
<script src="http://<vm-ip>/js/embed.min.js" data-isso="http://<vm-ip>/"></script> <section id="isso-thread" data-title="My first post"></section>data-isso tells the widget where the comment server lives. data-title names the thread.
Set data-title. If you omit it, Isso creates the thread by fetching your page to read its <title>. That means your site must be reachable from the VM at that moment. Supplying the title avoids the fetch entirely and is the more robust choice.
Each page gets its own thread automatically, keyed by its URL path. To show a comment count on an index page without loading the whole widget, use the separate counter script and link to the thread anchor:
<script src="http://<vm-ip>/js/count.min.js"></script> <a href="/my-post/#isso-thread">Comments</a>This VM serves a live demo of the widget on its own front page at http://<vm-ip>/, with your embed snippet shown alongside it.
Step 7 - Moderate your comments
Browse to http://<vm-ip>/admin/ and sign in with the password from Step 3.
The dashboard lists every comment with its thread, author and timestamp, and gives you Edit and Delete on each one. The filter bar switches between Valid, Pending and Staled, and you can order by date, likes or dislikes, group by thread, or search by URL.
The dashboard opens on the Pending filter, which is empty until you enable the moderation queue below. Select Valid to see published comments.
Enable the moderation queue
By default a new comment is published immediately. To hold every new comment until you approve it, set enabled = true in the [moderation] section of /etc/isso/isso.cfg and restart Isso. New comments then appear under Pending and stay invisible to readers until you activate them.
This affects only comments posted after the change. Existing comments stay published.
Step 8 - Back up your comments
The entire data tier is one SQLite file, so a backup is one command:
sudo sqlite3 /var/lib/isso/comments.db ".backup '/tmp/isso-backup.db'"ls -lh /tmp/isso-backup.dbCopy that file off the VM with scp. It contains everything: every thread, every comment and the admin session signing key.
To restore, stop Isso, putting the file back at /var/lib/isso/comments.db with isso:isso ownership, and start Isso again.
Use .backup rather than copying the file directly. It takes a consistent snapshot of a live database.
Step 9 - Serve comments over HTTPS
If your site uses HTTPS, browsers block requests to a plain-HTTP comment server as mixed content, so put a certificate in front of Isso. Point a DNS record at the VM, then run:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d comments.example.comThen add the HTTPS URL to [general] host, update data-isso in your template to the https:// address, and restart Isso.
When Isso is served over HTTPS, it automatically switches its session cookie to SameSite=None; Secure. This allows the widget to work when your site and your comment server are on different domains.
Troubleshooting
The widget shows nothing on my site. The cause is almost always Step 5: your site's URL is not in [general] host. Check your browser's console for a CORS error, add the URL, and restart Isso.
Isso will not start. Run sudo systemctl status isso and sudo journalctl -u isso -n 30. The preflight refuses to start on an empty, default or short admin password, on an empty [general] host, and on upstream's published default salt. The message names which one. Isso itself also exits if [general] host is empty.
"unable to connect to your website" in the log. Isso HEAD-checks each configured host at start-up. It is a warning, not a failure, and commenting still works, but it means none of the listed URLs answered from the VM. Note that the VM cannot reach its own public IP by design (see Step 5).
A comment fails with "URI is not accessible and no title was provided". The thread did not exist yet and Isso could not fetch the page to read its title. Add data-title to your <section id="isso-thread"> (Step 6).
I lost the admin password. Set a new one in [admin] password in /etc/isso/isso.cfg and restart Isso. Use at least 20 characters, because the preflight rejects anything shorter.
Comments are being rejected as spam. [guard] rate-limits to 2 new comments per minute per client subnet and 3 top-level comments per thread. Raise ratelimit and direct-reply if that is too strict for your traffic.
What is installed
The image contains the following components:
- Isso: 0.14.0, MIT licence, in a virtualenv at /opt/isso
- Application server: gunicorn, bound to 127.0.0.1:8080
- Web server: nginx on port 80, the only public listener
- Database: SQLite at /var/lib/isso/comments.db
- Configuration: /etc/isso/isso.cfg, mode 0640 root:isso
- Credentials: /root/isso-credentials.txt, mode 0600 root:root
- First boot: isso-firstboot.service, which rotates the admin password and salt per VM
- Preflight: /usr/local/sbin/isso-preflight.sh, which runs on every start of isso.service
The configuration file holds the admin password in plain text, because Isso compares it as a plain string and has no hashed form. It is readable only by root and the isso service account, and it is not reachable over HTTP.
Support
This image is packaged and maintained by SecureAnyCloud. Isso itself is free software under the MIT licence, developed by Martin Zimmermann and the Isso contributors. SecureAnyCloud is not affiliated with the Isso project.
For image-specific questions, contact SecureAnyCloud support through the Azure Marketplace listing. For questions about Isso itself, see the Isso documentation.
Deploy on Azure
Launch Isso on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This image runs Isso 0.14.0, the open-source, self-hosted commenting server by Martin Zimmermann, on Ubuntu 24.04 LTS. Isso gives any page on your website a comment thread that you own. You drop a single script tag into your template, and readers get threaded discussions, replies, Markdown, voting and email notifications, while every comment lives in a database on your own instance.
What you get:
- A deliberately small stack. The server is a Python WSGI application running under gunicorn bound to
127.0.0.1:8080, behind nginx on port 80. The entire data tier is one SQLite file. There is no separate database server, no cache tier, and no third-party service holding your readers' conversations or profiling them for advertising. The application has no public listener of its own, so nginx is the only front door. - Secure by default. Isso's own shipped configuration contains a published default admin password (
please_choose_a_strong_password). This image never ships it. On the first boot of every instance,isso-firstboot.servicegenerates two values unique to that instance: a 32-character admin password, which is the only credential for the moderation interface, and a per-instance email hash salt, so that commenter email hashes cannot be recovered with a rainbow table built against the salt every other Isso install shares. - An empty password that authenticates nobody. The image is captured with the admin password empty. Isso's login only proceeds
if data["password"] and data["password"] == password, so an empty submitted field is rejected before comparison and an empty configured password can never be matched. A fixed default value would be worse, because a default published in a public image is itself a known credential. - Guards that refuse to start. Isso will refuse to start until first boot has generated real values, on two independent levers.
isso.servicedeclares a hardRequiresdependency on the rotation. A preflight check re-runs on every start and refuses an empty, default or short password, which also catches a hand-edited configuration and not only a failed first boot. - No database in the image. This is a security control rather than housekeeping. Isso signs its admin session cookie with a
session-keythat it generates withos.urandomand stores inside the SQLite database, once, when the database is first created. An image that shipped a database would give every instance the same signing key, and anyone could forge an administrator session against any instance built from it. Because the image carries no database, Isso mints a fresh signing key on your instance's first start.
Connecting to your instance
Connect over SSH on port 22. On Ubuntu 24.04, the default SSH login user is ubuntu.
ssh -i /path/to/your-key.pem ubuntu@<instance-public-ip>The comment server itself is served on port 80. Reach it in a browser at http://<instance-public-ip>/. Never open port 8080 in your security group: gunicorn is bound to loopback and nginx is the only intended entry point.
Step 1 - Launch the instance
Launch the AMI from the AWS Marketplace listing, or with the AWS CLI. Isso is small, so m5.large is the recommended type and is ample for a busy site.
In your security group, open 22 (SSH) and 80 (HTTP), plus 443 if you will terminate TLS on the instance. To launch with the AWS CLI, run:
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=isso}]'Note the public IP address assigned to the instance. This guide uses it as <instance-public-ip> throughout.
Step 2 - Confirm the services are running
Connect over SSH as ubuntu and confirm that all three units are active. isso-firstboot is a oneshot unit, so active here means it ran to completion and rotated this instance's secrets.
systemctl is-active isso nginx isso-firstbootThe expected output is:
active active activeConfirm that Isso itself is bound only to loopback, and that nginx is the only public listener:
sudo ss -tlnp | grep -E ':80 |:8080 'The expected state is 127.0.0.1:8080 for the Python application and 0.0.0.0:80 for nginx. If gunicorn were listening on 0.0.0.0:8080, the application would be reachable around nginx.
Step 3 - Retrieve this instance's admin password
The admin password was generated on this instance's first boot and written to a root-owned file with mode 0600. Confirm that the file exists:
sudo ls -l /root/isso-credentials.txtThen read it:
sudo cat /root/isso-credentials.txtThe file contains the site URL, the admin URL and ISSO_ADMIN_PASSWORD. There is no username. Isso's admin interface is authenticated by the password alone.
Store the password in your password manager. Nothing else on the instance will show it to you again in plain text, and the value is unique to this instance.
Step 4 - Verify the server is answering
Isso exposes its effective configuration at /config. This is the appliance's health endpoint:
curl -s http://<vm-ip>/configNow confirm the endpoint that the comment widget itself uses. Isso's comment-fetch endpoint is the site root, with a uri query parameter naming the page whose thread you want. This is the exact request your readers' browsers make:
curl -s 'http://<vm-ip>/?uri=%2F' | head -c 200The response is JSON with a replies array, not HTML. Finally, prove that this instance's password authenticates. A 302 is a successful sign-in. Isso re-serves the login form with 200 when a password is wrong:
curl -s -o /dev/null -w "%{http_code}\n" -X POST -F "password=<isso-admin-password>" http://<vm-ip>/login/Step 5 - Add your website to the whitelist
This is the step that makes Isso work on your own site, and skipping it is the most common reason a freshly deployed widget stays empty.
Isso only accepts comments from sites you explicitly list. This is a deliberate security boundary. It stops anyone else embedding your comment server on their pages. On first boot the image whitelists this instance's own addresses so that the built-in demo works immediately, but your site is not in that list yet.
Open the configuration:
sudo nano /etc/isso/isso.cfgFind [general] host and add your site's URL, one per line, indented. Keep the existing entries. List a non-SSL URL first if you serve both, because some browsers suppress the referrer on plain HTTP requests:
[general] host = http://<instance-public-ip>/ https://blog.example.com/ http://blog.example.com/Restart Isso, then confirm that it connected to a configured site. Isso HEAD-checks each host at start-up, so give it a few seconds before reading the log:
sudo systemctl restart isso sleep 10
sudo journalctl -u isso -o cat -n 20 | grep -iE "connected to|unable to connect"connected to ... is the healthy state. unable to connect to your website means Isso could not reach any URL in the list from the instance. Check DNS, and check that the site is reachable from this network.
Why one of the listed URLs is this instance's private address. An EC2 instance cannot reach its own public IP, because traffic to the public address is translated at the internet gateway and is not routed back to the instance. Isso HEAD-checks each configured host at start-up and warns if none answer. The image therefore lists the private address as well, which the instance can always reach. It also lets you serve readers over the VPC without a public IP at all.
Step 6 - Embed the widget in your site
Add these two lines to your page template, wherever you want the thread to appear. This is the whole integration.
<script src="http://<instance-public-ip>/js/embed.min.js" data-isso="http://<instance-public-ip>/"></script> <section id="isso-thread" data-title="My first post"></section>data-isso tells the widget where the comment server lives. data-title names the thread.
Set data-title. If you omit it, Isso creates the thread by fetching your page to read its <title>. That means your site must be reachable from the instance at that moment. Supplying the title avoids the fetch entirely and is the more robust choice.
Each page gets its own thread automatically, keyed by its URL path. To show a comment count on an index page without loading the whole widget, use the separate counter script and link to the thread anchor:
<script src="http://<instance-public-ip>/js/count.min.js"></script> <a href="/my-post/#isso-thread">Comments</a>This instance serves a live demo of the widget on its own front page at http://<instance-public-ip>/, with your embed snippet shown alongside it.
Step 7 - Moderate your comments
Browse to http://<instance-public-ip>/admin/ and sign in with the password from Step 3.
The dashboard lists every comment with its thread, author and timestamp, and gives you Edit and Delete on each one. The filter bar switches between Valid, Pending and Staled, and you can order by date, likes or dislikes, group by thread, or search by URL.
The dashboard opens on the Pending filter, which is empty until you enable the moderation queue below. Select Valid to see published comments.
Enable the moderation queue
By default a new comment is published immediately. To hold every new comment until you approve it, set enabled = true in the [moderation] section of /etc/isso/isso.cfg and restart Isso. New comments then appear under Pending and stay invisible to readers until you activate them.
This affects only comments posted after the change. Existing comments stay published.
Step 8 - Back up your comments
The entire data tier is one SQLite file, so a backup is one command:
sudo sqlite3 /var/lib/isso/comments.db ".backup '/tmp/isso-backup.db'"ls -lh /tmp/isso-backup.dbCopy that file off the instance with scp. It contains everything: every thread, every comment and the admin session signing key.
To restore, stop Isso, putting the file back at /var/lib/isso/comments.db with isso:isso ownership, and start Isso again.
Use .backup rather than copying the file directly. It takes a consistent snapshot of a live database.
Step 9 - Serve comments over HTTPS
If your site uses HTTPS, browsers block requests to a plain-HTTP comment server as mixed content, so put a certificate in front of Isso. Point a DNS record at the instance, then run:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d comments.example.comThen add the HTTPS URL to [general] host, update data-isso in your template to the https:// address, and restart Isso.
When Isso is served over HTTPS, it automatically switches its session cookie to SameSite=None; Secure. This allows the widget to work when your site and your comment server are on different domains.
Troubleshooting
The widget shows nothing on my site. The cause is almost always Step 5: your site's URL is not in [general] host. Check your browser's console for a CORS error, add the URL, and restart Isso.
Isso will not start. Run sudo systemctl status isso and sudo journalctl -u isso -n 30. The preflight refuses to start on an empty, default or short admin password, on an empty [general] host, and on upstream's published default salt. The message names which one. Isso itself also exits if [general] host is empty.
"unable to connect to your website" in the log. Isso HEAD-checks each configured host at start-up. It is a warning, not a failure, and commenting still works, but it means none of the listed URLs answered from the instance. Note that the instance cannot reach its own public IP by design (see Step 5).
A comment fails with "URI is not accessible and no title was provided". The thread did not exist yet and Isso could not fetch the page to read its title. Add data-title to your <section id="isso-thread"> (Step 6).
I lost the admin password. Set a new one in [admin] password in /etc/isso/isso.cfg and restart Isso. Use at least 20 characters, because the preflight rejects anything shorter.
Comments are being rejected as spam. [guard] rate-limits to 2 new comments per minute per client subnet and 3 top-level comments per thread. Raise ratelimit and direct-reply if that is too strict for your traffic.
What is installed
The image contains the following components:
- Isso: 0.14.0, MIT licence, in a virtualenv at /opt/isso
- Application server: gunicorn, bound to 127.0.0.1:8080
- Web server: nginx on port 80, the only public listener
- Database: SQLite at /var/lib/isso/comments.db
- Configuration: /etc/isso/isso.cfg, mode 0640 root:isso
- Credentials: /root/isso-credentials.txt, mode 0600 root:root
- First boot: isso-firstboot.service, which rotates the admin password and salt per instance
- Preflight: /usr/local/sbin/isso-preflight.sh, which runs on every start of isso.service
The configuration file holds the admin password in plain text, because Isso compares it as a plain string and has no hashed form. It is readable only by root and the isso service account, and it is not reachable over HTTP.
Support
This image is packaged and maintained by SecureAnyCloud. Isso itself is free software under the MIT licence, developed by Martin Zimmermann and the Isso contributors. SecureAnyCloud is not affiliated with the Isso project.
For image-specific questions, contact SecureAnyCloud support through the AWS Marketplace listing. For questions about Isso itself, see the Isso documentation.
Deploy on AWS
Launch Isso with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com