Oqtane 10.2.6 on AWS User Guide
Overview
This guide covers launching and using the Oqtane 10.2.6 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg BookStack, CMS, Blogs and Wikis listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Oqtane is a free and open source content management system and modular application framework for .NET. Sites are built from pages, themes and modules (rich text, files, forms and your own Blazor modules), with users, roles and permissions, multiple sites per installation, search, localisation and a full administration area. It runs on ASP.NET Core and Blazor.
The cloudimg image adds:
- No reachable installer, no shared database. A fresh Oqtane opens its installation wizard to whoever visits first. This image ships no database, no encryption keyring and no installation settings at all. Your instance's first boot generates the host (superuser) password and runs Oqtane's own unattended installer before the web server starts, as the unprivileged
oqtaneuser on a temporary local port; the installer process is then stopped and checked to be gone. - Secrets of your instance's own, never on a command line. The host password, the ASP.NET Core data protection keys that protect sign-in cookies, and the TLS key are generated on your instance; nothing from the image build is reused. The password reaches the installer through its environment only, never as a command line argument, and is never logged.
- A patched runtime and patched libraries. Oqtane runs on the ASP.NET Core 10.0.14 runtime from Ubuntu's main archive, the October 2026 .NET security release. The Oqtane 10.2.6 release bundles
Microsoft.AspNetCore.Components.WebAssembly.Server10.0.12 (two high severity advisories, GHSA-g27p-qf6j-c96c and GHSA-qjpx-jqm5-h2c4) andSixLabors.ImageSharp4.1.1 (four high severity image-processing advisories). On this image they are replaced by the official NuGet packages of the fixed releases (the WebAssembly packages 10.0.14, ImageSharp 4.1.3), checked against NuGet's published checksums, inside the version ranges Oqtane itself declares; Oqtane's dependency manifest names exactly those versions. - Closed by default. Public registration is off, and Oqtane makes no calls home: the module and theme marketplace, the update check and the installation registration, which contact www.oqtane.net, are switched off (Step 8 shows how to switch them back on).
- HTTPS from the first boot. nginx serves Oqtane on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- Fail closed. nginx and Oqtane do not start until first boot has finished; if first boot cannot complete, the site stays offline rather than open.
- A rate-limited sign in. nginx allows 10 attempts a minute from one address (after a burst of 10) at every sign-in, two-factor, password reset, login link and registration route, in every spelling Oqtane accepts (any letter case, doubled or trailing slashes, a site path prefix), using the address of the connection, never a header a client sends. The host password is 25 random characters.
- Requests from other sites cannot act for you. Every request that changes something (any method but GET) and every WebSocket connection, including Blazor's interactive circuit, must come from your site's own address. The links that start a server-side download from a URL, a framework upgrade, a package install or a scheduled job only work when they come from your site itself (Step 10).
- The real host and client address. nginx hands Oqtane the host name you configure and nothing a request claims, so the site's links, sitemap and password reset e-mails use your host name, and Oqtane's logs record the address of the connection.
- Hardened defaults. Oqtane's web server (Kestrel) listens on the loopback interface only, behind nginx; it runs as the
oqtaneuser with systemd's sandboxing. Oqtane, nginx and the first boot service are blocked from the instance metadata service, the AMI requires IMDSv2, and requests for a host name the instance does not serve are dropped. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (Oqtane) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep 22 to your own networks. Open 443 to your visitors for a public site, or only to your team while you build it.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Oqtane 10.2.6 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=oqtane}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Oqtane 10.2.6 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes about a minute (Oqtane's installer creates the database and the default site). Then Oqtane and nginx are active, and the first boot and hairpin units are enabled:
systemctl is-active oqtane nginx
systemctl is-enabled oqtane nginx oqtane-firstboot oqtane-hairpin
active
active
enabled
enabled
enabled
enabled
Only SSH and nginx listen on the network; Oqtane's Kestrel server listens on the loopback interface only:
sudo ss -lntp | awk 'NR==1 || /:22 |:80 |:443 |:5000 /' | sed -E 's/users:\(\("([a-z]+)".*/\1/'
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* sshd
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* nginx
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* nginx
LISTEN 0 512 127.0.0.1:5000 0.0.0.0:* dotnet
LISTEN 0 4096 [::]:22 [::]:* sshd
LISTEN 0 511 [::]:80 [::]:* nginx
LISTEN 0 511 [::]:443 [::]:* nginx
The runtime and the release:
dotnet --list-runtimes
grep -m1 . /usr/share/doc/oqtane/RELEASE
Microsoft.AspNetCore.App 10.0.14 [/usr/lib/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 10.0.14 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]
Oqtane 10.2.6 (github.com/oqtane/oqtane.framework, MIT), Oqtane.Framework.10.2.6.Install.zip sha256 6cdb0091ecffb9dcdf10d821c5cf7674fbb7b6676208f56a1df77e8677646473
Step 4: Get your sign-in details
The credentials file holds the address Oqtane is served as (your instance's public DNS name by default), the sign-in address, the host user and its password, all generated on this instance. Show it with:
sudo cat /root/oqtane-credentials.txt
OQTANE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/
OQTANE_LOGIN_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login
OQTANE_ADMIN_USER=host
OQTANE_ADMIN_EMAIL=admin@example.com
OQTANE_ADMIN_PASSWORD=<12 letters and digits>-<12 letters and digits>
host is Oqtane's host account: the superuser of the installation, with every site's administration and the system pages (System Info, System Update, Sql Management). There is no other login. Its e-mail starts as admin@example.com; change it to your own address in your profile (click host at the top right) and configure SMTP in Admin Dashboard > Site Settings before you rely on password reset e-mails.
Step 5: Visit the site and sign in
Open OQTANE_URL in a browser. The certificate was generated on your instance, so the browser warns that it does not know the issuer; compare its SHA-256 fingerprint with the one in Step 10 and continue (or install your own certificate, Step 11). The default site opens:

Choose Login (or open OQTANE_LOGIN_URL), enter host and the password from Step 4:

Once you are signed in, the gear button at the top right opens the control panel, and /admin opens the Admin Dashboard:

System Info (/admin/system) shows the framework version, the .NET runtime and where Oqtane keeps its files:

Step 6: Edit content
Turn on edit mode with the pencil button at the top right, then use a module's menu to Edit it. Text modules open Oqtane's rich text editor (formatting toolbar, links, images and an HTML source view):

Save publishes the change at once:

Add pages in Admin Dashboard > Page Management, upload files in File Management, and manage users and roles in User Management and Role Management. Each module, page and folder carries its own view and edit permissions.
Step 7: Your data
Oqtane keeps its database (SQLite), its data protection keys and the site's uploaded files on the instance:
sudo ls -la /var/lib/oqtane/data /var/lib/oqtane/.aspnet/DataProtection-Keys | sed -E 's/key-[0-9a-f-]+\.xml/key-<id>.xml/'
/var/lib/oqtane/.aspnet/DataProtection-Keys:
total 12
drwx------ 2 oqtane oqtane 4096 Oct 31 15:32 .
drwx------ 3 oqtane oqtane 4096 Oct 31 15:31 ..
-rw------- 1 oqtane oqtane 1001 Oct 31 15:32 key-<id>.xml
/var/lib/oqtane/data:
total 500
drwxr-x--- 2 oqtane oqtane 4096 Oct 31 15:50 .
drwxr-x--- 4 oqtane oqtane 4096 Oct 31 15:31 ..
-rw-r--r-- 1 oqtane oqtane 499712 Oct 31 15:50 Oqtane.db
/var/lib/oqtane/data/Oqtane.db: pages, modules, content, users, settings and logs./var/lib/oqtane/.aspnet/DataProtection-Keys: the keys that protect sign-in cookies and other protected values. Back them up with the database; without them every user has to sign in again./opt/oqtane/app/Content: uploaded files of each site (created when you upload the first file)./opt/oqtane/app: the Oqtane application. It belongs to theoqtaneuser, because Oqtane installs modules, themes and framework upgrades into it.
For a consistent backup stop Oqtane, copy those paths, and start it again (sudo systemctl stop oqtane, then sudo systemctl start oqtane), or take an EBS snapshot of the volume. Oqtane can also use SQL Server, MySQL or PostgreSQL instead of SQLite (see Oqtane's documentation); this image is configured for SQLite.
Step 8: The closed-by-default settings
Public registration is off. To let visitors create accounts, open Admin Dashboard > Site Settings and turn on Allow Registration.
Oqtane's marketplace of modules and themes, its update check and the installation registration contact www.oqtane.net. They are off on this image, because PackageRegistryUrl is empty in /opt/oqtane/app/appsettings.Production.json:
sudo grep -n 'PackageRegistryUrl' /opt/oqtane/app/appsettings.Production.json
8: "PackageRegistryUrl": "",
To switch them on, set the value to https://www.oqtane.net and restart Oqtane:
sudo sed -i 's#"PackageRegistryUrl": ""#"PackageRegistryUrl": "https://www.oqtane.net"#' /opt/oqtane/app/appsettings.Production.json
sudo systemctl restart oqtane
Modules and themes can always be installed from a package file you upload in Module Management or Theme Management. Install only packages you trust: a module is .NET code that runs inside Oqtane.
Step 9: Behind a proxy or load balancer
nginx hands Oqtane the configured host name, https and the address of the connection, and drops every forwarding header a client sends. Behind an Application Load Balancer, terminate TLS on the load balancer, forward to port 443 on the instance and set the instance's host name to the name your users browse to (Step 11). Oqtane then records the load balancer's address as the client; to log the visitor's address instead, use nginx's real_ip module with set_real_ip_from limited to your load balancer's subnets.
Step 10: Security checks
Port 80 redirects to HTTPS, and a request for any other host name is dropped (curl reports 000). Each block reads your host name from the credentials file first:
H="$(sudo sed -n 's#^OQTANE_URL=https://\([^/]*\)/$#\1#p' /root/oqtane-credentials.txt)"
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' "http://${H}/login"
curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login
000
The certificate is an end-entity certificate for your host name (it cannot sign other certificates). Compare its fingerprint with the one your browser shows:
sudo openssl x509 -in /etc/nginx/ssl/oqtane.crt -noout -subject -ext basicConstraints,extendedKeyUsage -fingerprint -sha256
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = cloudimg
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Extended Key Usage:
TLS Web Server Authentication
sha256 Fingerprint=<your instance's fingerprint>
Oqtane's server-side components call Oqtane's own API at your host name, so the instance trusts this one certificate for itself (/usr/local/share/ca-certificates/cloudimg-oqtane-instance.crt) and keeps those calls on the instance. Do not add it to the trust store of your workstation: compare the fingerprint instead, or use a certificate from a public authority (Step 11).
Kestrel cannot be reached on the instance's own network address, and Oqtane, nginx and first boot are blocked from the instance metadata service:
curl -s -m 5 -o /dev/null -w '%{http_code}\n' "http://$(hostname -I | awk '{print $1}'):5000/" || true
systemctl show -p IPAddressDeny oqtane nginx oqtane-firstboot
000
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
Oqtane links and its sitemap use the configured https host, whatever a request claims:
H="$(sudo sed -n 's#^OQTANE_URL=https://\([^/]*\)/$#\1#p' /root/oqtane-credentials.txt)"
curl -s -H 'X-Forwarded-Host: evil.example' -H 'X-Forwarded-Proto: http' "https://${H}/robots.txt"; echo
User-agent: *
Sitemap: https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/sitemap.xml
A cross-origin request that changes something, and a cross-site link to a guarded route, are refused by nginx (403):
H="$(sudo sed -n 's#^OQTANE_URL=https://\([^/]*\)/$#\1#p' /root/oqtane-credentials.txt)"
curl -s -o /dev/null -w '%{http_code}\n' -H 'Origin: https://evil.example' -d '{}' "https://${H}/api/user/logout"
curl -s -o /dev/null -w '%{http_code}\n' -H 'Sec-Fetch-Site: cross-site' "https://${H}/api/job/start/1"
403
403
The sign-in limit applies per network address. From the instance's own network address (the instance's loopback traffic is Oqtane's own and is not limited), the first attempts reach Oqtane (400: these test requests carry no form token) and the rest are refused with 429:
H="$(sudo sed -n 's#^OQTANE_URL=https://\([^/]*\)/$#\1#p' /root/oqtane-credentials.txt)"
P="$(hostname -I | awk '{print $1}')"
for i in $(seq 1 14); do curl -s -o /dev/null -w '%{http_code} ' --interface "${P}" --resolve "${H}:443:${P}" -H "Origin: https://${H}" -d 'username=x&password=y' "https://${H}/pages/login/"; done; echo
400 400 400 400 400 400 400 400 400 400 400 429 429 429
Step 11: Your own DNS name and certificate
Point a DNS name at the instance's public address (an Elastic IP keeps it stable), then make it the name Oqtane is served as. The helper mints a new certificate for it, pins nginx and the request guards to it, renames the site's alias and restarts Oqtane:
sudo oqtane-set-hostname www.example.com
To use a certificate from a public authority (for example from Let's Encrypt with certbot's webroot or DNS method), replace /etc/nginx/ssl/oqtane.crt and /etc/nginx/ssl/oqtane.key with your full chain and key, keep /usr/local/share/ca-certificates/cloudimg-oqtane-instance.crt in step with the new certificate (or remove it once the certificate chains to a public root) and reload nginx:
sudo nginx -t && sudo systemctl reload nginx
With a trusted certificate in place you can add Strict-Transport-Security in /etc/nginx/snippets/cloudimg-oqtane-headers.conf (it is left out while the certificate is self-signed).
Step 12: Passwords, e-mail, upgrades and modules
- Password: change the host password in your profile (click host, then Profile), and keep
/root/oqtane-credentials.txtup to date or delete it. - E-mail: configure SMTP in Admin Dashboard > Site Settings > SMTP Settings; Oqtane sends password reset, verification and notification e-mail through it.
- Upgrades: Oqtane upgrades itself from System Update once the package registry is switched on (Step 8), or from an upgrade package you upload. Take a backup first (Step 7). cloudimg also publishes new versions of this image.
- Logs: Admin Dashboard > Event Log shows Oqtane's own log (sign-in failures included); the service log is
sudo journalctl -u oqtane.
Step 13: Server components
dpkg-query -W -f='${Package} ${Version}\n' aspnetcore-runtime-10.0 nginx sqlite3
grep -E '^ (Microsoft|SixLabors)' /usr/share/doc/oqtane/RELEASE | cut -d'(' -f1
aspnetcore-runtime-10.0 10.0.14-0ubuntu1~24.04.1
nginx 1.24.0-2ubuntu7.19
sqlite3 3.45.1-1ubuntu2.8
Microsoft.AspNetCore.Components.WebAssembly.Server 10.0.14
Microsoft.AspNetCore.Components.WebAssembly 10.0.14
Microsoft.JSInterop.WebAssembly 10.0.14
Microsoft.AspNetCore.Components.WebAssembly.Authentication 10.0.14
SixLabors.ImageSharp 4.1.3
| Component | Where |
|---|---|
| Oqtane 10.2.6 | /opt/oqtane/app, service oqtane (Kestrel on 127.0.0.1:5000) |
| nginx | /etc/nginx/sites-available/cloudimg-oqtane, TLS in /etc/nginx/ssl |
| Database and keys | /var/lib/oqtane |
| First boot | oqtane-firstboot.service, /usr/local/sbin/oqtane-firstboot.sh |
| Helpers | /usr/local/sbin/oqtane-set-hostname, /usr/local/sbin/oqtane-cred-roundtrip |
Step 14: Security recommendations
- Allow port 22 in the security group from your own networks only.
- Change the host e-mail to your own (Step 4), keep its password secret and change it if it may have been shared (Step 12). Create separate administrator accounts for the people who manage a site, and keep the host account for system tasks.
- Open registration only when you are ready to moderate it (Step 8).
- Keep the database and the data protection keys secret and backed up (Step 7).
- Use your own DNS name and a certificate your browsers trust (Step 11).
- Keep Oqtane on the newest release (Step 12) and install only modules and themes you trust.
- Keep the operating system patched; unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else (the .NET runtime included).
Support and licensing
Oqtane is free software under the MIT License (a copy is at /usr/share/doc/oqtane/LICENSE). ImageSharp is under the Six Labors Split License, which grants the Apache License 2.0 for use in open source software such as Oqtane (/usr/share/doc/oqtane/SIXLABORS-IMAGESHARP-LICENSE); the other bundled libraries carry their own open source licences. There is no per user or per CPU licence fee and no licence key. This image installs the upstream release with the bundled WebAssembly and ImageSharp libraries raised to their fixed releases, plus the cloudimg first boot, settings and helpers described above. The AMI price covers cloudimg's hardening, first boot setup and support. cloudimg is not affiliated with or endorsed by the Oqtane project or the .NET Foundation.
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
- Support: 24/7, with a 24 hour response SLA