Scoold 1.70.2 on AWS User Guide
Overview
This guide covers launching and using the Scoold 1.70.2 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.
Scoold is an open source Q&A and knowledge-base platform in the style of Stack Overflow. Your team asks questions, writes answers, votes, comments, tags posts and earns reputation and badges; moderators edit, close, merge and delete posts, and the administration area shows activity, users and spaces. Scoold stores everything through Para, an open source backend server that runs alongside it on the same instance with an embedded H2 database and a Lucene search index, so no content leaves your instance.

What is on the image:
- Scoold 1.70.2 and Para 1.57.0 (both Apache-2.0), the upstream release jars verified by SHA-256, with 19 bundled libraries (Tomcat, Jackson, Bouncy Castle, Guava and others) replaced by their fixed upstream releases from Maven Central; the list is in
/usr/share/doc/scoold/library-updates.txt - OpenJDK 21 from Ubuntu's main archive
- Scoold as the unprivileged
scoolduser on127.0.0.1:8000, and Para as the unprivilegedparauser on127.0.0.1:8080with its H2 database server on127.0.0.1:9092, under the systemd unitsscoold.serviceandpara.service - nginx as the only network-facing service: HTTPS on port 443 with a self-signed certificate created for your instance on first boot, port 80 redirecting to HTTPS
- A per-instance administrator password, generated on first boot into a root-only file
scoold-set-hostname, which serves Scoold under your own DNS name and re-issues the certificate for it, andscoold-conf-set, which changes one configuration setting safely
Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3
Security posture
- Every secret is made on your instance. Para's sample configuration ships a published database password (
secret) and a default salt, and Scoold's quick start uses a well-known root key. On each instance's first boot this image generates Para's root app secret, the H2 database password, Para's signing key, Scoold's session signing key and a 28-character administrator password. Para then mints the key pair Scoold uses to reach it, and the root app secret is removed from Scoold's configuration. Secrets are written only to files readable by root and the service that needs them, never passed on a command line. - No default login, no open sign-up. The only account is the administrator
admin@scoold.localwith its per-instance password. A new password sign-up cannot sign in until its e-mail address is verified, and the image ships no mail server settings, so nobody can register themselves until you configure SMTP (Step 6). - Cross-site requests are refused. Scoold has no anti-CSRF tokens, so nginx refuses every state-changing request (anything but
GET,HEADandOPTIONS) whoseOriginorReferernames another site. Pages that act on a plainGET(the two-factor QR code, the data export and the sign-in callback) are also refused when a browser marks the request cross-site. - The setup wizard, REST API and MCP server are closed. Scoold's
/startonboarding pages include unauthenticated connection tests that make the server call any address; nginx answers404for/start,/api,/apidocsand/mcp, and they are switched off in Scoold's configuration as well. - The address comes from configuration, never from a request. First boot reads the instance's public DNS name from EC2 instance metadata (IMDSv2) and writes it into Scoold's and nginx's configuration. nginx answers only for that name (any other
Hostheader gets its connection closed), replaces any client-suppliedX-Forwarded-For,X-Real-IPorForwardedheader with the real peer address, and Scoold trusts forwarding headers from127.0.0.1only. - Rate-limited sign-in, hardened cookies. nginx limits sign-in attempts per address (
429after a short burst). The session cookie isSecure,HttpOnlyandSameSite=Lax, and URLs never carry a session id. - Fails closed.
scoold-preflightruns before Para, before Scoold and before nginx on every start. It refuses missing, default or short secrets, a non-HTTPS public address, a Para endpoint off loopback, the setup wizard, API or MCP server switched on, an H2 server that would accept remote connections or create new databases, and readable configuration. nginx also waits for first boot to finish and for the instance's own certificate. - Only nginx faces the network. Scoold, Para and the H2 database listen on loopback only. nginx uses TLS 1.2 or 1.3 and sends HSTS.
- No metadata access from the services.
para.service,scoold.serviceandnginx.serviceare denied the instance metadata service with systemdIPAddressDeny, and the AMI is registered withImdsSupport v2.0, so instances refuse token-less (IMDSv1) metadata requests. Both Java services also run with no capabilities, a read-only system and no core dumps. - Per-instance identity. SSH host keys, the machine ID and the TLS certificate are generated fresh on every instance.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
Recommended instance type: m5.large, the listing default. Scoold and Para are two Java services, so give the instance at least 4 GiB of memory (t3.medium or larger); the image refuses to start on less.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| Scoold 1.70.2 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened Discourse, Mastodon & Forums and Social Networks AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose Scoold 1.70.2 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (the software charge is $0.04 per vCPU per hour) and your key pair.
- Security group: the listing's default group opens SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and restrict 443 and 80 to the networks your team uses if the site is internal.
- Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and HTTPS from anywhere, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR with your own:
SG=$(aws ec2 create-security-group --group-name scoold \
--description "Scoold Q&A" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=scoold}]'
You do not need --metadata-options: the AMI already requires IMDSv2. Associate an Elastic IP if you want the address to survive a stop and start, and point a DNS name at it (Step 7).
Step 2: Connect and find your login
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>
First boot takes two to three minutes. It sets the name Scoold is served as (the instance's public DNS name when it has one), creates the TLS certificate for it, generates every secret, starts Para, lets Scoold register itself with Para, creates the administrator and signs in once to prove the password, and writes /root/scoold-credentials.txt (mode 0600, root). Only then does nginx open port 443. These are the entries in that file (the password is not shown here):
sudo grep -E '^SCOOLD_(URL|ADMIN_EMAIL)=' /root/scoold-credentials.txt
sudo grep -oE '^[A-Z0-9_]+=' /root/scoold-credentials.txt
sudo stat -c '%a %U:%G' /root/scoold-credentials.txt
SCOOLD_URL=https://ec2-44-203-84-35.compute-1.amazonaws.com/
SCOOLD_ADMIN_EMAIL=admin@scoold.local
SCOOLD_URL=
SCOOLD_ADMIN_EMAIL=
SCOOLD_ADMIN_PASSWORD=
SCOOLD_TLS_SHA256=
600 root:root
| Value | What it is for |
|---|---|
SCOOLD_URL |
The address of your Scoold site |
SCOOLD_ADMIN_EMAIL |
The administrator's sign-in e-mail |
SCOOLD_ADMIN_PASSWORD |
The administrator password, generated on this instance |
SCOOLD_TLS_SHA256 |
The SHA-256 fingerprint of the instance's certificate, to check the browser warning against |
To read the password, run sudo cat /root/scoold-credentials.txt in your own session and store it in your password manager.
Step 3: Confirm Scoold is running
First boot, Para, Scoold and nginx are active. Only sshd and nginx listen beyond loopback: Scoold, Para and the H2 database server are on 127.0.0.1 (Java reports its IPv4 loopback sockets as [::ffff:127.0.0.1]). Para's health endpoint answers on loopback:
systemctl is-active scoold-firstboot.service para.service scoold.service nginx.service
for j in /opt/scoold/scoold.jar /opt/para/para.jar; do
python3 -I -c 'import sys, zipfile; m = zipfile.ZipFile(sys.argv[1]).read("META-INF/MANIFEST.MF").decode(); print(*[l.split(": ")[1] for l in m.splitlines() if l.startswith(("Implementation-Title", "Implementation-Version"))])' "$j"
done
java -version 2>&1 | head -1
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u
curl -s http://127.0.0.1:8080/v1/_health; echo
active
active
active
active
Scoold 1.70.2
para-server 1.57.0
openjdk version "21.0.12.1" 2026-08-18
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.53%lo:53 users:(("systemd-resolve"))
127.0.0.54:53 users:(("systemd-resolve"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
[::ffff:127.0.0.1]:8000 users:(("java"))
[::ffff:127.0.0.1]:8080 users:(("java"))
[::ffff:127.0.0.1]:9092 users:(("java"))
{"message":"healthy"}
Step 4: Prove the login and the web defaults
A signed-out visit to /admin is sent to the sign-in page, a wrong password gets no session, a form POST from another site is refused, the setup wizard and the REST API are closed, an unknown Host header gets no answer, and plain HTTP redirects to HTTPS. The last line runs the image's own round-trip check, which signs in with this instance's password without printing it:
H=$(sudo sed -n 's/^set \$scoold_host "\(.*\)";$/\1/p' /etc/nginx/scoold-host.conf)
R=(--resolve "$H:443:127.0.0.1")
curl -sk "${R[@]}" -o /dev/null -w 'admin page, signed out -> %{http_code}\n' "https://$H/admin"
curl -sk "${R[@]}" -o /dev/null -D /tmp/scoold-login.hdr -w 'wrong password -> %{http_code}' -H "Origin: https://$H" \
--data-urlencode provider=password --data-urlencode access_token=password \
--data-urlencode username=admin@scoold.local --data-urlencode 'password=not-the-password' "https://$H/signin"
echo ", session cookies issued: $(grep -ci '^set-cookie: *scoold-auth=[^;]' /tmp/scoold-login.hdr)"; rm -f /tmp/scoold-login.hdr
curl -sk "${R[@]}" -o /dev/null -w 'cross-origin POST -> %{http_code}\n' -H 'Origin: https://evil.example' --data 'x=1' "https://$H/questions/ask"
curl -sk "${R[@]}" -o /dev/null -w 'setup wizard /start -> %{http_code}\n' "https://$H/start/test-para-connection"
curl -sk "${R[@]}" -o /dev/null -w 'REST API /api -> %{http_code}\n' "https://$H/api/stats"
curl -sk -o /dev/null -w 'unknown Host -> %{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
curl -s -o /dev/null -w 'plain http -> %{http_code}\n' -H "Host: $H" http://127.0.0.1/
sudo /usr/local/sbin/scoold-roundtrip.sh
admin page, signed out -> 303
wrong password -> 303, session cookies issued: 0
cross-origin POST -> 403
setup wizard /start -> 404
REST API /api -> 404
unknown Host -> 000
plain http -> 301
OK round-trip https://ec2-44-203-84-35.compute-1.amazonaws.com/: home 200; anonymous /admin -> sign-in; a wrong password gets no session; the per-VM password -> a Secure HttpOnly SameSite=Lax scoold-auth session that opens /admin (200); a cross-origin sign-in POST is refused (403)
Step 5: Sign in and start your knowledge base
Browse to SCOOLD_URL. The certificate is self-signed until Step 7, so your browser warns once; you can compare the fingerprint it shows with SCOOLD_TLS_SHA256. Choose Sign in and enter admin@scoold.local and SCOOLD_ADMIN_PASSWORD:

Choose Ask Question to post: give it a title, write the body in Markdown and add tags. Answers, comments and votes work the same way, and the question shows its answers, tags and similar posts:

The administrator's menu (your avatar, top right) opens Administration: a dashboard of questions, answers, active users and top topics, plus pages for spaces, users, reports and settings. Use Spaces to separate teams or projects:

Step 6: Let your team sign up
Password sign-in is on, and a new account may sign in only once its e-mail address is verified. The image ships with no mail server, so verification e-mails cannot be sent yet and nobody can register themselves:
for k in scoold.password_auth_enabled scoold.security.allow_unverified_emails scoold.mail.host; do
printf '%s = %s\n' "$k" "$(sudo scoold-conf-set /var/lib/scoold/application.conf "$k" --get)"
done
scoold.password_auth_enabled = true
scoold.security.allow_unverified_emails = false
scoold.mail.host =
To open sign-up, give Scoold your SMTP server. Put the SMTP password in a root-only file first, so it never appears on a command line, then set the values and restart Scoold:
sudo scoold-conf-set /var/lib/scoold/application.conf scoold.mail.host --value smtp.example.com
sudo scoold-conf-set /var/lib/scoold/application.conf scoold.mail.port --value 587
sudo scoold-conf-set /var/lib/scoold/application.conf scoold.mail.username --value scoold@example.com
sudo scoold-conf-set /var/lib/scoold/application.conf scoold.mail.password --value-file /root/smtp-password.txt
sudo scoold-conf-set /var/lib/scoold/application.conf scoold.support_email --value scoold@example.com
sudo systemctl restart scoold
New users then choose Sign up, confirm the e-mail Scoold sends, and sign in. To restrict who can join, set scoold.approved_domains_for_signups to your company's e-mail domains, or add single sign-on through LDAP or an OAuth 2.0 / OpenID Connect provider from Scoold's configuration reference. Setting scoold.security.allow_unverified_emails to true lets anyone who can reach the site register without a mailbox check, so only do that on a private network.
Step 7: Use your own DNS name and certificate
Point a DNS record (for example qa.example.com) at the instance's Elastic IP, then tell Scoold the name. The helper re-issues the self-signed certificate for it, updates nginx's accepted name and both cross-site guards, sets Scoold's public address, records the name in /etc/cloudimg/scoold-hostname (kept across reboots), updates SCOOLD_URL and restarts Scoold:
sudo scoold-set-hostname qa.example.com
scoold-set-hostname: Scoold is served at https://qa.example.com/
Until you set a name, the instance follows its own public DNS name: if it comes back from a stop and start with a new address, the certificate and names are updated at boot.
Your team's browsers need a certificate they trust. Replace /etc/ssl/scoold/scoold.crt and /etc/ssl/scoold/scoold.key with one from your own CA or from Let's Encrypt (keep the key mode 0600, owner root), set the name with scoold-set-hostname first so the automatic refresh leaves your certificate alone, and run sudo systemctl reload nginx. For Let's Encrypt, port 80 already serves /.well-known/acme-challenge/ from /var/www/letsencrypt, so after sudo apt install certbot, sudo certbot certonly --webroot -w /var/www/letsencrypt -d qa.example.com works without stopping nginx; copy or link the issued files into place and reload nginx after each renewal.
Step 8: Change the administrator password
Signed in as the administrator, open your avatar menu, choose Settings and use Password > Change: enter the current password and a new one. Two-Factor Authentication on the same page adds a TOTP code from an authenticator app to every sign-in, and is worth turning on for the administrator:

Then delete /root/scoold-credentials.txt (sudo rm /root/scoold-credentials.txt) or update its password line, so it does not hold a stale password.
Step 9: Check the security controls
The certificate was made for this instance on first boot, Para and Scoold run as their own users with no new privileges and no core dumps, all three services are denied the metadata service, the configuration files and TLS key are closed to other users, and the preflight accepts the current configuration for each service:
H=$(sudo sed -n 's/^set \$scoold_host "\(.*\)";$/\1/p' /etc/nginx/scoold-host.conf)
openssl s_client -connect 127.0.0.1:443 -servername "$H" </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate
systemctl show -p User,IPAddressDeny,NoNewPrivileges,LimitCORE para.service scoold.service
systemctl show -p IPAddressDeny --value nginx.service
sudo stat -c '%a %U:%G %n' /etc/para/application.conf /var/lib/scoold/application.conf /var/lib/para /etc/ssl/scoold/scoold.key
for m in para scoold nginx; do sudo scoold-preflight "$m"; done
subject=CN = ec2-44-203-84-35.compute-1.amazonaws.com, O = cloudimg
notAfter=Feb 10 00:20:56 2029 GMT
IPAddressDeny=168.63.129.16/32 169.254.169.254/32
LimitCORE=0
User=para
NoNewPrivileges=yes
IPAddressDeny=168.63.129.16/32 169.254.169.254/32
LimitCORE=0
User=scoold
NoNewPrivileges=yes
168.63.129.16/32 169.254.169.254/32
640 root:para /etc/para/application.conf
600 scoold:scoold /var/lib/scoold/application.conf
750 para:para /var/lib/para
600 root:root /etc/ssl/scoold/scoold.key
scoold-preflight (para): OK
scoold-preflight (scoold): OK
scoold-preflight (nginx): OK
Step 10: Back up your knowledge base
Every question, answer, user and setting is in Para's H2 database and Lucene index under /var/lib/para, and the keys that open them are in the two configuration files. Stop Scoold and Para for a consistent copy (about a minute of downtime), archive all three into a root-only file, and start them again:
sudo systemctl stop scoold.service para.service
sudo sh -c 'umask 077 && tar -C / -czf /root/scoold-backup.tgz var/lib/para etc/para/application.conf var/lib/scoold/application.conf'
sudo systemctl start para.service scoold.service
sudo tar -tzf /root/scoold-backup.tgz | grep -E '(para\.mv\.db|application\.conf)$'
sudo stat -c '%a %U:%G %n' /root/scoold-backup.tgz
for i in $(seq 1 90); do curl -sf -o /dev/null http://127.0.0.1:8000/ && break; sleep 2; done
systemctl is-active para.service scoold.service
var/lib/para/data/para.mv.db
etc/para/application.conf
var/lib/scoold/application.conf
600 root:root /root/scoold-backup.tgz
active
active
The archive holds every secret of the instance, so keep it as carefully as the instance itself. Copy it off the instance (for example sudo cp /root/scoold-backup.tgz /tmp/ && sudo chown ubuntu /tmp/scoold-backup.tgz, then scp from your workstation) and delete the copies on the instance afterwards. To restore on this instance, stop Scoold and Para, run sudo tar -C / -xzpf scoold-backup.tgz, and start Para and then Scoold. EBS snapshots of the root volume also capture everything.
Maintenance
- Logs:
sudo journalctl -u scooldandsudo journalctl -u parafor the applications,/var/log/nginx/for HTTPS access,sudo journalctl -u scoold-firstbootfor first boot. - Restart:
sudo systemctl restart para scoold, orsudo systemctl restart nginx. Each runsscoold-preflightfirst. - Settings: Scoold's options are in
/var/lib/scoold/application.confand Para's in/etc/para/application.conf(see the Scoold configuration reference). Change one withsudo scoold-conf-set <file> <key> --value <value>(or--value-filefor a secret) and restart the service; the preflight refuses an unsafe setting and names it. Settings changed from Administration > Configuration in the browser are saved to the same file. - Operating system updates: Ubuntu's unattended security upgrades stay on, so nginx, OpenJDK and the rest of the OS keep receiving patches. Apply the rest with
sudo apt update && sudo apt upgradein a maintenance window. - Upgrading Scoold or Para: read the Scoold and Para release notes and take a backup first. Stop both services, replace
/opt/scoold/scoold.jaror/opt/para/para.jarwith the new release jar (owner root, mode 0644), and start Para and then Scoold.
Troubleshooting
The browser says the connection is not private. The certificate is self-signed until you install your own (Step 7). Check that the fingerprint the browser shows matches SCOOLD_TLS_SHA256, then continue.
Sign-in is refused or answers 429. Use admin@scoold.local and SCOOLD_ADMIN_PASSWORD from /root/scoold-credentials.txt exactly, over https://. After several quick attempts nginx answers 429 for a minute; wait and try again.
A form shows 403 Forbidden. nginx refuses form submissions whose Origin or Referer is another site. Open Scoold at the address in SCOOLD_URL (or the name you set with scoold-set-hostname), not through a different name or an IP address, and submit again.
A new user cannot sign in after signing up. Their e-mail address is not verified yet. Configure SMTP (Step 6) so Scoold can send the confirmation e-mail.
Scoold, Para or nginx does not start. sudo journalctl -u scoold --no-pager | tail -20 (or -u para, -u nginx) shows the preflight's reason, for example a short secret, a world-readable configuration or the API switched on. Fix the setting and run sudo systemctl restart para scoold nginx.
Support
cloudimg provides 24/7 support for this image by email and live chat: launching and connecting, sign-in and sign-up, e-mail, TLS and hostnames, backups and upgrades.
Scoold and Para are developed by Erudika and released under the Apache License 2.0. The licence texts and a pointer to the source ship on the instance in /usr/share/doc/scoold/ and /usr/share/doc/para/. This image contains only the open source community editions. Ubuntu is a registered trademark of Canonical Ltd. All other product and company names are trademarks or registered trademarks of their respective holders. cloudimg is not affiliated with or endorsed by them.