OpenOLAT 21.1.1 on AWS User Guide
Overview
This guide covers launching and using OpenOLAT 21.1.1 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.
OpenOLAT is a mature open source learning management system used by universities, schools and training organisations. It brings course design and delivery together in one web platform: a visual course editor with learning paths, SCORM, xAPI and IMS content packages, tests and a shared question bank built on QTI 2.1, assessment and grading, groups with forums, wikis and chat, e-portfolios, curriculum management, certificates and badges, and virtual classroom integrations.
OpenOLAT is free software published under the Apache License 2.0; the licence and the project's NOTICE file ship in the image at /opt/openolat/LICENSE and /opt/openolat/NOTICE.TXT. OpenOlat and the OpenOlat logo are trademarks of frentix GmbH, Zurich; cloudimg is not affiliated with, endorsed by, or sponsored by frentix GmbH. This image packages the upstream open source release together with cloudimg's provisioning, hardening and support.
What is included:
- OpenOLAT 21.1.1 from the official release (
openolat_2111.war, built from the upstreamOpenOLAT_21.1.1tag), deployed at/opt/openolat/webappwith its file store at/var/lib/openolat/olatdata - A dedicated Apache Tomcat 10.1 on Ubuntu's OpenJDK 21, listening on
127.0.0.1:8088only, with a Java heap sized automatically at 40% of the instance's memory - A local PostgreSQL 16 database, bound to
127.0.0.1only - nginx serving OpenOLAT over HTTPS at
https://<instance-address>/; port 80 only ever redirects to HTTPS and serves no content of its own - A per-instance administrator password, database password, maintenance tokens, installation identifiers and HTTPS certificate, all generated on first boot and written to a root-only file. No default, shared or published credential ships in the image, and the web server does not start until first boot has finished
- The database schema is created during the image build, so there is no setup wizard and nothing to configure before you sign in
- Self-registration, guest access and the REST API are switched off until you choose to enable them
- 24/7 cloudimg support

Security posture
- No default login. OpenOLAT seeds a built-in
administratoraccount with a published default password. That default never exists in this image: the build seeds the account with a random value, and the captured image carries no password hash at all for it. Each instance's first boot sets its own administrator password, and its own PostgreSQL role password, before the application is allowed to start. - No build-era database state. The PostgreSQL cluster that ran during the build is destroyed before capture; the image carries a fresh cluster with the application data restored into it and a database role that has no password until first boot sets one.
- Fails closed. Tomcat 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. Tomcat (8088) and PostgreSQL (5432) listen on
127.0.0.1only, and Tomcat has no shutdown port and no sample applications. - The site address comes from configuration, never from the request. On every boot
openolat-baseurl.servicesets OpenOLAT'sserver.domainname, nginx's allowed names and the certificate from/etc/cloudimg/openolat-hostnameif set, otherwise from IMDSv2 (public DNS name, else public IPv4, else private IPv4). nginx answers only those names and closes the connection for any otherHost, and it always hands Tomcat the configured name, so a forgedHostorX-Forwarded-Hostcannot steer the links OpenOLAT builds into notification and password-reset mail. - Real client addresses and a cross-site guard. nginx overwrites
X-Forwarded-Forwith the real peer address, and Tomcat 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. OpenOLAT can be asked to fetch remote URLs (LTI tools and platforms, RSS and podcast feeds, external pages and videos, OpenID Connect discovery, AI provider endpoints, Microsoft 365 integration), so
openolat.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). Only the first-boot and base-URL units read metadata, once, for the site address. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances refuse token-less (IMDSv1) 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. - Audited Java libraries. The 335 libraries OpenOLAT bundles were checked against the OSV vulnerability database at build time. Two moderate advisories in the release (Netty's DNS decoder and the OpenTelemetry API) are fixed by replacing those libraries with their patched releases, and the image ships a checker that proves the libraries on disk are exactly the audited set (Step 13).
- Patched at build time. The image is a full
apt full-upgradewith all security updates; unattended security upgrades stay on.
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 or 8088
Recommended instance type: m5.large (2 vCPU, 8 GiB RAM). OpenOLAT is a Java application with a local database, and on 8 GiB the Java heap grows to about 3.2 GB automatically. Use m5.xlarge or larger for large course catalogues, many concurrent learners, or heavy video and SCORM use.
Step 1: Launch from AWS Marketplace
- Open the Hardened Moodle & E-Learning Platforms AMIs listing by cloudimg and choose View purchase options, then subscribe.
- Choose Continue to Configuration. Under Software version choose
OpenOLAT 21.1.1 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 two to three minutes.
Step 2: Launch from the AWS CLI
Workstation commands. Replace <ami-id> with the AMI ID shown for the OpenOLAT 21.1.1 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 openolat-sg \
--description "OpenOLAT" --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=openolat}]'
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 OpenOLAT stack is running
First boot generates this instance's secrets and only then starts the application and the web server, so allow two or three minutes after launch. When the three units are active, the platform is ready:
systemctl is-active postgresql openolat nginx
active
active
active
Confirm OpenOLAT answers over HTTPS on the instance itself and check the release. The certificate is self-signed per instance, so -k is expected here until you install your own (Step 11):
curl -sk https://127.0.0.1/dmz/ | grep -oE 'OpenOlat( | )[0-9.]+' | sed 's/ / /' | sort -u
OpenOlat 21.1.1

Step 5: Check what the network can reach
Only SSH and the two web ports listen on the instance's network interfaces. Tomcat and PostgreSQL are bound to the loopback address, and systemd's DNS stub resolver is local only:
sudo ss -H -lnt | 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.1:8088
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80
Port 80 serves nothing but a permanent redirect to HTTPS:
curl -s -o /dev/null -w '%{http_code} -> %{redirect_url}\n' http://127.0.0.1/
301 -> https://127.0.0.1/

The application cannot reach the instance metadata service. The first request below runs from inside openolat.service as the openolat user and fails (000, blocked); the second runs from your shell and gets a token (200):
CG=$(systemctl show -p ControlGroup --value openolat.service)
sudo bash -c "echo \$\$ > /sys/fs/cgroup${CG}/cgroup.procs; exec setpriv --reuid=openolat --regid=openolat --init-groups curl -s -m 5 -o /dev/null -w '%{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token"
curl -s -m 5 -o /dev/null -w '%{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token
000
200
Step 6: Retrieve the first-boot credentials
Every secret on this instance was generated on its first boot and written to a file only root can read. Nothing here is shared with any other instance:
sudo cat /root/openolat-credentials.txt
# OpenOLAT 21.1.1 on Ubuntu 24.04 by cloudimg - per-VM credentials (generated at first boot)
# Sign in at the URL below as 'administrator'. Your browser will warn about the
# self-signed certificate until you install a certificate for your own domain.
OPENOLAT_URL=https://<public-dns>/
openolat.admin.user=administrator
openolat.admin.pass=<generated-per-instance>
openolat.db.name=openolat
openolat.db.user=openolat
openolat.db.pass=<generated-per-instance>
administrator is OpenOLAT's built-in system administrator account. First boot records each step in its log:
sudo tail -n 3 /var/log/cloudimg-firstboot.log
[...] openolat-firstboot: writing /root/openolat-credentials.txt
[...] openolat-firstboot: secrets minted - releasing the web stack
[...] openolat-firstboot: first boot complete - OpenOLAT is at https://<public-dns>/

If you keep credentials centrally, copy them into AWS Secrets Manager.
Step 7: Sign in
Browse to https://<public-dns>/. Your browser warns about the self-signed certificate; accept it to continue, or install your own certificate first (Step 11). OpenOLAT signs you in in two steps: enter the username administrator and select Login, then enter the password from Step 6. The first time you sign in, OpenOLAT asks you to accept its terms of use.
You can also prove the credential from the instance itself. The image includes a small checker that signs in through the real OpenOLAT login form, reading the password from standard input so it never appears on a command line:
sudo sed -n 's/^openolat\.admin\.pass=//p' /root/openolat-credentials.txt | sudo /usr/local/sbin/openolat-login-check administrator
LOGIN_OK
OpenOLAT's published default password is rejected:
printf 'openolat' | sudo /usr/local/sbin/openolat-login-check administrator || true
LOGIN_REJECTED

After signing in you land on your course list:

Change the administrator password after your first sign-in: open the menu under your initials at the top right and choose Password. OpenOLAT requires at least 12 characters with a letter and a digit or symbol.
Step 8: Create your first course
Choose Authoring in the top navigation, then Create and Course. Give the course a title and, if you like, a reference code, keep the default course design (With learning path) and select Create:

OpenOLAT opens the new course's settings, and the course appears in Authoring under My entries with the status Preparation:

Open Administration in the course toolbar and choose Course editor to add content, for example a Page for the introduction, a Forum for discussion and a Folder for course materials. Publish the changes, and when you are ready for learners change the course Status from Preparation to Published and add participants under Administration, Members management.
Step 9: Add users and review the system
Choose User management in the top navigation to create accounts one by one or import a list, and assign roles such as author, coach or learner administrator. OpenOLAT can also sign users in through LDAP, Shibboleth or OpenID Connect identity providers; see the OpenOLAT administrator manual.
The system Administration area shows the running release, memory use and active users, and holds every platform setting:

Step 10: Configure outbound email
OpenOLAT sends account, notification and password-reset mail. Outbound mail is switched off in this image until you configure a relay. EC2 throttles outbound port 25, so use an authenticated provider such as Amazon SES on port 587. Open Administration, Core functions, E-mail, enter your provider's host, port, username and password, then send yourself a test message. Because the site address is pinned to this instance, the links in that mail always point back at your OpenOLAT.
Step 11: Use your own hostname and certificate
By default the instance serves on its EC2 public DNS name. To serve on your own DNS name, point the name at the instance (ideally an Elastic IP) and run:
sudo openolat-set-hostname lms.example.com
This writes /etc/cloudimg/openolat-hostname, rewrites OpenOLAT's server.domainname, nginx's allowed names and the self-signed certificate, and restarts OpenOLAT (a few minutes). The setting persists across reboots. sudo openolat-set-hostname --auto returns to the EC2 name.
With the name resolving publicly, obtain a trusted certificate. Certbot's nginx plugin installs it and renews it automatically:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d lms.example.com
Keep 80 and 443 reachable from the internet for the HTTP-01 challenge.
Step 12: Configuration posture
Self-registration, guest access and the REST API are off by default, and the public address comes from configuration:
sudo grep -E '^(server\.domainname|registration\.enableSelfRegistration|login\.enableGuestLoginLinks|restapi\.enable|smtp\.host)=' /opt/openolat/lib/olat.local.properties
server.domainname=<public-dns>
smtp.host=disabled
registration.enableSelfRegistration=false
login.enableGuestLoginLinks=false
restapi.enable=false
Turn self-registration on under Administration, Login, Self-registration, and guest access under Anonymous and external user, only when you intend to accept public sign-ups or anonymous visitors.
Step 13: Bundled library audit
The image ships the list of the 335 Java libraries it was audited with (name and sha256 of each) under /usr/share/doc/openolat/jar-audit/, together with a checker. It proves the libraries OpenOLAT loads are exactly that set:
sudo /usr/local/sbin/openolat-jar-audit
JAR-AUDIT shipped set == audited set (335 jars, sha256 each)
With --online it also queries the OSV vulnerability database for every audited library (after first proving the database answers for a known-vulnerable control), so you can re-check the image at any time. The two libraries replaced by cloudimg are listed in /usr/share/doc/openolat/jar-audit/jar-swaps.txt:
ls /opt/openolat/webapp/WEB-INF/lib | grep -E 'netty-codec-dns|opentelemetry-api'
netty-codec-dns-4.1.136.Final.jar
opentelemetry-api-1.62.0.jar
Step 14: Memory and logs
The Java heap follows the instance size, so changing the instance type is all it takes to give OpenOLAT more memory:
ps -o args= -u openolat | tr ' ' '\n' | grep -E '^-XX:(Max|Initial)RAMPercentage'
-XX:InitialRAMPercentage=12
-XX:MaxRAMPercentage=40
OpenOLAT's application log is /opt/openolat/logs/olat.log and rolls over daily, keeping 30 days; Tomcat's own output goes to the systemd journal:
sudo tail -n 5 /opt/openolat/logs/olat.log
Step 15: Managing the services
sudo systemctl restart openolat # the application (Tomcat); takes a minute or two
sudo systemctl restart nginx # the HTTPS front end
sudo journalctl -u openolat -e # application output
sudo journalctl -u openolat-firstboot # first-boot log
Step 16: Back up your platform
OpenOLAT keeps state in two places: the PostgreSQL database and the file store at /var/lib/openolat/olatdata. Stop the application briefly for a consistent pair. The backups hold your learners' data and stored password hashes, so they are written root-only:
sudo systemctl stop openolat
sudo sh -c 'umask 077; sudo -u postgres pg_dump -Fc openolat > /var/backups/openolat-db-$(date +%F).dump'
sudo sh -c 'umask 077; tar czf /var/backups/openolat-files-$(date +%F).tar.gz -C /var/lib/openolat olatdata'
sudo systemctl start openolat
Copy both off the instance (for example to Amazon S3) and take EBS snapshots on a schedule and before any upgrade. To restore, stop OpenOLAT, load the dump with pg_restore --clean --if-exists -d openolat as the postgres user, unpack the file store over /var/lib/openolat, and start OpenOLAT again (tested on this image: a fresh instance's dump is about 3 MB and restores all 393 tables).
Step 17: Maintenance and upgrades
Ubuntu security updates, including OpenJDK, PostgreSQL and nginx, are applied automatically. To move to a later OpenOLAT 21 patch release, back up first (Step 16), then replace the web application with the new release from openolat.com/releases and restart; OpenOLAT upgrades its own database schema on start. A new upstream release bundles its own libraries, so re-run sudo /usr/local/sbin/openolat-jar-audit --online afterwards (it reports the new set as different from the audited one) or move to the cloudimg version that ships it.
Step 18: Troubleshooting
- Browser certificate warning: expected until Step 11 installs a trusted certificate.
- Site shows the wrong address in emails: set your DNS name with
openolat-set-hostname(Step 11). - Site is down right after launch: first boot has not finished; wait two to three minutes, then check
sudo journalctl -u openolat-firstboot. - Slow first page after a restart: Tomcat takes a minute or two to start OpenOLAT; watch
sudo journalctl -u openolat -f.
Step 19: Security recommendations
- Restrict
22/tcpto your management network and front the instance with your own DNS name and a trusted certificate (Step 11). - Change the administrator password after the first sign-in and create named accounts for staff rather than sharing the administrator.
- Keep the instance patched:
sudo apt update && sudo apt full-upgrade. - Library advisories. At build time no open advisory remained against the bundled libraries: the release's two moderate advisories (Netty DNS decoder memory leak CVE-2026-73508 and OpenTelemetry W3C baggage allocation CVE-2026-45292) are fixed by the replaced libraries in Step 13. Re-check with
openolat-jar-audit --onlinewhenever you like.
Step 20: Support and licensing
OpenOLAT is free software under the Apache License 2.0, published by frentix GmbH and the OpenOLAT community. cloudimg packages the upstream release with the provisioning and hardening described here and provides 24/7 support for this image by email and live chat: support@cloudimg.co.uk. For questions about OpenOLAT itself, see the OpenOLAT documentation.