SkillTree 5.0.0 on AWS User Guide
Overview
This guide covers launching and using SkillTree 5.0.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Moodle & E-Learning Platforms AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other learning platforms in the listing are described on the listing's guide hub.
SkillTree is an open source gamified training and micro-learning platform. You model a curriculum as projects made of subjects, skills, badges, quizzes and surveys, with points, levels and learning paths. Trainees see their progress, levels and rankings in the SkillTree dashboard, and your own applications can report skill events to SkillTree over its API so that training activity elsewhere earns points here.
SkillTree is free software published under the Apache License 2.0; the licence and the project's disclaimer ship in the image at /usr/share/doc/skilltree/. cloudimg is not affiliated with, endorsed by, or sponsored by the SkillTree project or its contributors. This image packages the upstream open source release together with cloudimg's provisioning, hardening, dependency fixes and support.
What is included:
- SkillTree 5.0.0 from the official release (
skills-service-5.0.0.jar, checksum-verified), laid out at/opt/skilltree/appand run as the unprivilegedskilltreeuser on127.0.0.1:8080 - Eclipse Temurin 25 (the Java runtime SkillTree 5.0.0 requires), from the Eclipse Adoptium repository, verified by its signing key and kept up to date by unattended upgrades
- A local PostgreSQL 16 database bound to
127.0.0.1only - nginx serving SkillTree over HTTPS at
https://<instance-address>/; port 80 only ever redirects to HTTPS - A per-instance administrator password, database password, JWT signing key and HTTPS certificate, all generated on first boot. No default, shared or published credential ships in the image, and the web server does not start until first boot has finished
- Public self-sign-up closed by default: you create trainee accounts with one command (Step 8)
- Patched libraries: Apache Tomcat 11.0.27, Jackson 2.21.7 and 3.1.7, jsoup 1.23.2 and DOMPurify 3.4.17 replace the vulnerable versions in the release (see Security posture)
- 24/7 cloudimg support

Security posture
- No default login. SkillTree starts in an "install mode" in which the first visitor can create the root administrator. That window never opens to the network here: first boot creates this instance's root administrator itself, on the loopback interface, with a generated password, before nginx is allowed to start. Install-mode paths are refused by nginx afterwards.
- No build-era database or secrets. The image carries no SkillTree database and no database role at all. First boot creates both with this instance's own password, mints the JWT signing keystore that replaces the example keystore upstream ships inside the release, and writes the configuration. A start-time guard refuses to run SkillTree with an empty, short or published secret.
- Self-registration is closed. Upstream lets anyone who can reach the site create an account. Here nginx refuses the sign-up endpoint from the network, and the instance administrator creates accounts with
sudo skilltree-create-user. You can deliberately reopen public sign-up with one command (Step 8). - Fixed libraries. The 5.0.0 release bundles libraries with published advisories. This image replaces them with the patched releases of the same lines, each checksum-pinned: Apache Tomcat embed 11.0.24 to 11.0.27 (three critical advisories), Jackson 2.21.5 to 2.21.7 and 3.1.5 to 3.1.7 (denial-of-service advisories reachable through JSON requests), jsoup 1.23.1 to 1.23.2, and the AWS SDK's bundled Jackson parser. The dashboard carried three copies of the DOMPurify HTML sanitiser (3.4.16 with a critical advisory, and two 2.3.3 copies inside the Toast UI editor and viewer); each is replaced by DOMPurify 3.4.17, and the rest of the dashboard is byte-identical to upstream. A checker in the image proves the libraries on disk are exactly the audited set and that the OSV vulnerability database reports no advisory for them (Step 12).
- Fails closed. SkillTree and nginx are held shut until first boot has minted this instance's secrets and certificate, so a half-provisioned instance stays down rather than open.
- Only the web tier faces the network. The listening set is exactly 22, 80 and 443. SkillTree (8080), its management port (8808) and PostgreSQL (5432) listen on
127.0.0.1only, and nginx does not proxy the actuator or any API-documentation path. - The site address comes from configuration, never from the request. On first boot and every later boot
skilltree-set-hostnamesets nginx's allowed name and the certificate from/etc/cloudimg/skilltree-hostnameif you set one, otherwise from IMDSv2 (public DNS name, else public IPv4, else private IPv4). nginx closes the connection for any otherHostand always hands SkillTree the configured name. - Real client addresses and a cross-site guard. nginx overwrites
X-Forwarded-Forwith the real peer address and SkillTree trusts forwarded headers from loopback only. A state-changing request or WebSocket upgrade whoseOriginis not this instance is refused with403. - No instance metadata access for the application. SkillTree can be configured to call outside services (an OpenAI-compatible AI provider for its assistant, identity providers), so
skilltree.service,nginx.serviceand the PostgreSQL server are blocked from the instance metadata endpoints (169.254.169.254, the IPv6 and IPv4-mapped forms, and the Azure wire-server). The block was proven through SkillTree's own AI-provider fetch at build time. Only the first-boot and address units read metadata, once, for the site address. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances refuse token-less metadata requests. - Key-only SSH. Password and keyboard-interactive logins, root login and X11 forwarding are off in
/etc/ssh/sshd_config.d/00-cloudimg.conf; you sign in asubuntuwith your EC2 key pair. - Patched at build time. The image is a full
apt full-upgrade; unattended upgrades stay on for Ubuntu and for the Temurin Java runtime.
Prerequisites
- An AWS account where you can subscribe to AWS Marketplace products and launch EC2 instances
- An EC2 key pair in the target Region
- A VPC and subnet, with a public IP address on the instance if your users reach it over the internet
- A security group that allows
22/tcpfrom your management network, and80/tcpand443/tcpfrom your users' networks. No other port is needed; do not open 5432, 8080 or 8808
Recommended instance type: m5.large (2 vCPU, 8 GiB RAM). SkillTree runs with a 1.5 GB Java heap and fits a 4 GiB instance for evaluation; use m5.large or larger for real cohorts, and m5.xlarge for many concurrent trainees or heavy video and attachment use.
Step 1: Launch from AWS Marketplace
- Open the Hardened Moodle & E-Learning Platforms AMIs listing by cloudimg, choose View purchase options, and subscribe.
- Choose Continue to Configuration. Under Software version choose
SkillTree 5.0.0 on Ubuntu 24.04, pick your Region, and choose Continue to Launch. - Under Choose Action select Launch through EC2.
- Set the instance type to m5.large, choose your key pair, and use the seller's security group settings (ports 22, 80 and 443), narrowing port 22 to your management network.
- Launch. First boot finishes within about two minutes.
Step 2: Launch from the AWS CLI
Workstation commands. Replace <ami-id> with the AMI ID shown for the SkillTree 5.0.0 on Ubuntu 24.04 software version on the listing's launch page, and use your own VPC, subnet, key pair and address ranges.
aws ec2 create-security-group --group-name skilltree-sg \
--description "SkillTree" --vpc-id <vpc-id>
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 22 --cidr <your-ip>/32
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 80 --cidr <users-cidr>
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 443 --cidr <users-cidr>
aws ec2 run-instances --image-id <ami-id> --instance-type m5.large \
--key-name <key-name> --subnet-id <subnet-id> \
--security-group-ids <security-group-id> --associate-public-ip-address \
--metadata-options HttpTokens=required \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=skilltree}]'
aws ec2 describe-instances --instance-ids <instance-id> \
--query "Reservations[].Instances[].[PublicDnsName,PublicIpAddress]" --output text
Step 3: Connect over SSH
ssh -i <key.pem> ubuntu@<public-dns>
The login user is ubuntu.
Step 4: Confirm the SkillTree stack is running
First boot generates this instance's secrets, creates the database, starts SkillTree (which builds its schema on the first start) and only then starts the web server, so allow about two minutes after launch. When the three units are active, the platform is ready:
systemctl is-active postgresql skilltree nginx
active
active
active
Confirm SkillTree answers over HTTPS on the instance itself and check the release. The certificate is self-signed per instance, so -k is expected until you install your own (Step 11):
curl -sk https://127.0.0.1/public/config | python3 -c 'import json,sys; c=json.load(sys.stdin); print("SkillTree", c["dashboardVersion"], "-", c["authMode"], "sign-in")'
SkillTree 5.0.0 - FORM sign-in
The runtime versions:
java -version 2>&1 | head -2; psql --version; nginx -v
openjdk version "25.0.5" 2026-10-20 LTS
OpenJDK Runtime Environment Temurin-25.0.5+8 (build 25.0.5+8-LTS)
psql (PostgreSQL) 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1)
nginx version: nginx/1.24.0 (Ubuntu)
Step 5: Check what the network can reach
sudo ss -Htln | awk '{print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:5432
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80
[::ffff:127.0.0.1]:8080
[::ffff:127.0.0.1]:8808
Only SSH, HTTP and HTTPS listen on the network; SkillTree, its management port and PostgreSQL are loopback-only. Port 80 answers nothing but a redirect to HTTPS on the configured name:
curl -sI http://127.0.0.1/public/config | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://<instance-address>/public/config
The Spring Boot actuator is not reachable through nginx:
curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/actuator/health
404
Step 6: Retrieve the first-boot credentials
First boot writes the sign-in details to a file only root can read:
sudo cat /root/skilltree-credentials.txt
# SkillTree 5.0.0 on Ubuntu 24.04 by cloudimg - credentials generated on this VM's first boot.
# Generated: 2026-10-26T22:07:39Z
# Readable by root only. Sign in at the URL below and change the password under your profile.
# The site uses a self-signed certificate created for this instance; your browser warns until you
# install your own. Own DNS name: sudo skilltree-set-hostname skills.example.edu
# Public sign-up is closed: create trainee accounts with sudo skilltree-create-user <email> <first> <last>
SKILLTREE_URL=https://<instance-address>/
SKILLTREE_ADMIN_USER=admin@skilltree.local
SKILLTREE_ADMIN_PASSWORD=<SKILLTREE_PASSWORD>
The password is unique to this instance. Keep it in your password manager and change it after your first sign-in.
Step 7: Sign in
Open SKILLTREE_URL in your browser. The certificate is self-signed for this instance, so accept the warning once (or install your own certificate, Step 11). Sign in as admin@skilltree.local with the password from Step 6.
This account is SkillTree's root user: it can administer every project, grant administrator roles to other users, and change platform settings under the user menu.
You can confirm the credential from the shell as well, without the password ever appearing on a command line:
sudo bash -c '. /usr/local/lib/cloudimg/skilltree-lib.sh; J=$(st_jar_new); sed -n "s/^SKILLTREE_ADMIN_PASSWORD=//p" /root/skilltree-credentials.txt | st_login "$J" admin@skilltree.local && echo "administrator sign-in OK"; rm -f "$J"'
administrator sign-in OK
Step 8: Create trainee accounts
Public sign-up is closed, so the sign-up form on the login page is refused by nginx:
sudo skilltree-self-registration status
curl -sk -o /dev/null -w '%{http_code}\n' -X PUT -H 'Content-Type: application/json' --data '{"email":"someone@example.com"}' https://127.0.0.1/createAccount
self-registration: disabled (administrator creates accounts)
403
Create each trainee from the shell. The tool generates a 24-character password, writes it to a file only root can read, and proves the new account signs in:
sudo skilltree-create-user alex.trainee@example.com Alex Trainee
created alex.trainee@example.com; its password is in /root/skilltree-user-alex.trainee_example.com.txt (root only). Ask the user to change it after the first sign-in.
Hand the user their password securely and delete the file afterwards (sudo shred -u /root/skilltree-user-*.txt). To make a trainee an administrator of a project, sign in as root, open the project, and add them on the project's Access page.
If you want people to register themselves (for example on a private network), reopen public sign-up deliberately, and close it again the same way:
sudo skilltree-self-registration enable
sudo skilltree-self-registration disable
Step 9: Build your first training project
In the dashboard choose Project to create a project, then add Subjects and Skills. Each skill has a point value and the number of repetitions needed to complete it; levels, badges, learning paths, quizzes and surveys build on top of those.

Skill descriptions are written in Markdown with a live preview; HTML in a description is sanitised on the server and again in the browser.


Trainees sign in and see their points, level, rank and history in Progress and Rankings:

Step 10: Report skill events from your applications
Your own applications can award skills when users complete activities elsewhere. Each project has a client ID and a client secret for its API (shown to project administrators in the dashboard). Use the official skills-client JavaScript libraries in a web application, or call the REST API from a back-end service, and point them at https://<instance-address>/. Keep client secrets out of browser code and source control.
Step 11: Use your own hostname and certificate
Point a DNS record at the instance, then pin SkillTree to that name. The tool updates nginx and the Origin guard and issues a new self-signed certificate for the name:
sudo skilltree-set-hostname skills.example.edu
skilltree-set-hostname --show
To use a certificate from your own CA or from Let's Encrypt, replace /etc/ssl/skilltree/skilltree.crt and /etc/ssl/skilltree/skilltree.key (key mode 0600, owner root) and run sudo systemctl reload nginx. sudo skilltree-set-hostname --auto returns to the EC2 address.
Step 12: Check the bundled libraries
The image ships the exact list of libraries that was audited at build time. The checker proves that every library on disk and every dashboard file is byte-identical to that list, and with --online asks the OSV vulnerability database about each of them (it first proves the query works on known-vulnerable versions):
sudo skilltree-dependency-audit --online
DEP-AUDIT shipped set == audited set: 299 jars (sha256 each), application jar content digest, 35 dashboard files; DOMPurify 3.4.17 x3, no other version
DEP-AUDIT OSV: 344 coordinates (Maven + npm), 0 advisory(ies), all documented (positive controls found)
The replaced libraries, their checksums and the audit lists are in /usr/share/doc/skilltree/dependency-audit/. Run the checker again after any change you make under /opt/skilltree; when a later SkillTree release is published, cloudimg publishes a new version on the listing.
Step 13: Memory, logs and services
SkillTree runs with a 1.5 GB Java heap (/etc/default/skilltree); raise -Xmx there on a larger instance and restart the service. No swap is configured.
free -m | head -2
total used free shared buff/cache available
Mem: 3832 2064 1295 42 751 1767
The application logs to the journal; nginx logs to /var/log/nginx/:
sudo journalctl -u skilltree -n 50 --no-pager
sudo systemctl restart skilltree
sudo systemctl status skilltree nginx postgresql
The configuration SkillTree reads is /etc/skilltree/application.properties (root and the skilltree group only). It holds this instance's database password and keystore password; keep its permissions as they are.
Step 14: Back up and restore
Back up the database and the per-instance configuration to files only root can read, then copy them off the instance (for example to Amazon S3 with your own credentials):
sudo bash -c 'umask 077; sudo -u postgres pg_dump -Fc skilltree > /root/skilltree-backup.dump'
sudo bash -c 'umask 077; tar czf /root/skilltree-config-backup.tgz -C / etc/skilltree var/lib/skilltree/jwtkeys.jks etc/ssl/skilltree root/skilltree-credentials.txt'
To restore onto a new instance launched from this AMI, copy the dump to it, stop SkillTree, restore the database, and start SkillTree again. The new instance keeps its own database password, signing key and certificate; your users, projects and progress come from the dump, so you sign in with your previous administrator password:
sudo systemctl stop skilltree
sudo bash -c 'sudo -u postgres pg_restore --clean --if-exists --no-owner --role=skilltree -d skilltree < /root/skilltree-backup.dump'
sudo systemctl start skilltree
The configuration archive keeps a copy of the old instance's settings and certificate for reference; restore individual files from it only if you need them.
cloudimg tested the backup on a fresh instance: the dump restored all 45 SkillTree tables and the projects into a scratch database. EBS snapshots of the root volume are a simple complement.
Step 15: Updates
Ubuntu security updates and the Temurin Java runtime are applied automatically by unattended upgrades:
apt-cache policy temurin-25-jre | sed -n '1,3p'
temurin-25-jre:
Installed: 25.0.5.0.0+8-1
Candidate: 25.0.5.0.0+8-1
A Java or PostgreSQL update needs a restart of SkillTree to take effect (sudo systemctl restart skilltree). New SkillTree releases arrive as new versions on the AWS Marketplace listing; move to one by launching it and restoring your backup (Step 14).
Step 16: Optional AI assistant
SkillTree 5.0.0 can use an OpenAI-compatible provider to help authors write skills and quizzes. It is off until you configure it: add your provider to /etc/skilltree/application.properties and restart SkillTree.
skills.openai.host=https://api.openai.com
skills.openai.key=<your-api-key>
The instance metadata block stays in force for the provider calls.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your instance ID and the AWS Region.