SkillTree 4.6.0 on Ubuntu 24.04 on Azure User Guide
Overview
SkillTree is a free and open source training gamification platform. You model a curriculum as projects, subjects, skills and badges, your applications report user activity to SkillTree over its REST API, and SkillTree awards points, levels and badges back and renders progress, rankings and metrics for trainees. It is designed to bolt gamified training onto an application you already have, rather than to replace it.
The cloudimg image installs SkillTree 4.6.0 from the official release artifact, pinned by SHA 256, and runs it as a Spring Boot service behind nginx with a local PostgreSQL. The Vue dashboard ships inside the same application artifact, so there is no separate frontend to deploy or keep in step.
What is included:
- SkillTree 4.6.0 from the official release, verified by SHA 256 and installed at
/opt/skilltree - The Vue dashboard bundled inside the same artifact and served on port 80
- OpenJDK 21 from the Ubuntu 24.04 archive, so the runtime keeps receiving security updates
- PostgreSQL from the Ubuntu 24.04 archive, on loopback only, tuned for a 4 GB VM
- nginx as the only public listener, with an unauthenticated
/healthzprobe - A JVM heap pinned to
-Xmx1536mso the whole stack fits Standard_B2s with no swap skilltree-firstboot.servicegenerating the administrator password, the database password and the JWT signing keystore uniquely per VM- A start time guard that refuses to start the service if any published example secret is in effect
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet with a subnet. Standard_B2s (2 vCPU, 4 GB RAM) is the tested size and runs the whole stack with roughly 2 GB of memory still available. Move to a larger size if you expect many concurrent trainees or heavy video and attachment uploads. Open port 80 on the network security group, plus 22 for SSH from your management address.
Step 1 to 3: Deploy and connect
Deploy the image from the Azure Marketplace, then connect over SSH as azureuser.
ssh azureuser@<vm-ip>
Step 4: Verify the services
SkillTree runs as a Spring Boot service behind nginx, with PostgreSQL alongside it.
sudo systemctl is-active postgresql skilltree nginx
java -version
sudo ss -tlnp | grep -E ':80 |:8080 '

Step 5: Read the first boot credentials
On the first boot of your VM a one shot service generates the administrator password, the PostgreSQL password and the JWT signing keystore, all unique to that VM, and writes the administrator credentials to a root only file. The image itself ships with no database and no account of any kind, so there is no default login to change.
sudo sed -E 's/(SKILLTREE_ADMIN_PASSWORD=).*/\1**** unique per VM ****/' /root/skilltree-credentials.txt
sudo ls -l /etc/skilltree/application.properties /var/lib/skilltree/jwtkeys.jks
The screenshot below masks the password, which is different on every VM.

Read the real password when you need it:
sudo grep SKILLTREE_ADMIN_PASSWORD /root/skilltree-credentials.txt
Step 6: Check the endpoints
nginx serves an unauthenticated health probe, and SkillTree serves its own public bootstrap configuration used by the dashboard.
curl -s -o /dev/null -w 'healthz %{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w 'public config %{http_code}\n' http://127.0.0.1/public/config
Step 7: Sign in to the dashboard
Browse to http://<your-vm-ip>/ and sign in with the administrator address admin@skilltree.local and the password from Step 5.

You can prove the same sign in from the command line. This performs a real form login, keeps the session cookie, and then uses that cookie to call an authenticated endpoint.
JAR=$(mktemp)
curl -s -o /dev/null -w 'login %{http_code}\n' -c "$JAR" \
-X POST http://127.0.0.1/performLogin \
--data-urlencode 'username=<SKILLTREE_USER>' \
--data-urlencode 'password=<SKILLTREE_PASSWORD>'
curl -s -b "$JAR" http://127.0.0.1/app/userInfo | cut -c1-120; echo
rm -f "$JAR"

Step 8: Create your first project
A project is one gamified training profile. Create one from the dashboard with the Project button, or over the REST API as below.
JAR=$(mktemp)
curl -s -o /dev/null -c "$JAR" -X POST http://127.0.0.1/performLogin \
--data-urlencode 'username=<SKILLTREE_USER>' \
--data-urlencode 'password=<SKILLTREE_PASSWORD>'
XSRF=$(awk '$6=="XSRF-TOKEN"{print $7}' "$JAR")
curl -s -o /dev/null -w 'create project %{http_code}\n' -b "$JAR" \
-H "X-XSRF-TOKEN: $XSRF" -H 'Content-Type: application/json' \
-X POST http://127.0.0.1/app/projects/onboarding \
--data '{"projectId":"onboarding","name":"New Starter Onboarding"}'
curl -s -o /dev/null -w 'pin project %{http_code}\n' -b "$JAR" \
-H "X-XSRF-TOKEN: $XSRF" -X POST http://127.0.0.1/root/pin/onboarding
rm -f "$JAR"
The pin step matters. Your administrator is a SkillTree root user, and a root user's Projects page lists only the projects they have pinned, so a newly created project will not appear until it is pinned. You can also pin an existing project from the dashboard with the Pin button.

Step 9: Add subjects and skills
Subjects group skills inside a project, and skills are what your application reports against. Each skill defines how many points it awards and how many times it must be performed.

Open a subject to manage its skills. SkillTree keeps the running point total for the subject and the project as you add them.

Step 10: Report a skill event from your application
Your application tells SkillTree that a user did something. Points, levels and badges follow automatically. Reporting uses a project token, which you create under the project's Access page.
sudo -u postgres psql -d skilltree -tAc \
"select project_id, name, total_points from project_definition order by project_id"
sudo -u postgres psql -d skilltree -tAc "select count(*) from skill_definition"

Memory and swap
The JVM heap is deliberately capped so the whole stack fits the Standard_B2s envelope. Azure certification does not permit a swap file baked into the image, so this image ships with none and does not need any.
grep SKILLTREE_JVM_OPTS /etc/default/skilltree
free -m | head -2
swapon --show
If you move to a larger VM size and want a bigger heap, edit -Xmx in /etc/default/skilltree and restart the service. Keep the heap well under half of the VM's memory so PostgreSQL and the page cache still have room.
Managing the service
sudo systemctl status skilltree --no-pager | head -8
sudo systemctl is-enabled skilltree nginx postgresql
Restart SkillTree after a configuration change with sudo systemctl restart skilltree, and follow its log with sudo journalctl -u skilltree -f.
The service will refuse to start if its configuration is missing a generated secret or is pointing at a published example value. That guard runs on every start, so a misconfigured VM fails loudly rather than coming up with a weak secret.
Security recommendations
- Change the administrator password after your first sign in, from Settings then Security.
- Put your own TLS terminator in front of port 80 before exposing this VM to the internet, and restrict port 22 to your management address. SkillTree's session cookie is deliberately not marked secure so that sign in works over the appliance's plain HTTP front door; once you terminate TLS in front of it, set
server.servlet.session.cookie.secure=truein/etc/skilltree/application.propertiesand restart the service. - Keep PostgreSQL on loopback. The image does not expose it, and there is no reason to.
- The Spring Boot management port 8808 is bound to loopback and is authenticated. Leave it that way.
- Take regular backups of the
skilltreedatabase withpg_dumpif you rely on the training records.
Next steps
Point your application at the REST API to report skill events as your users complete work, and use the dashboard's Metrics pages to see progress across projects, subjects and user tags. Upstream project documentation and the API reference live with the SkillTree project at github.com/NationalSecurityAgency/skills-service, which is the source of the software in this image.
Support
cloudimg provides 24/7 support for this image. Contact support through the cloudimg website with your VM's region, size and the output of sudo systemctl status skilltree --no-pager.