Deployment guide

Umbraco 17.8.0

Overview

Umbraco is a free and open-source content management system for .NET. Editors build pages from document types and templates, write in a rich-text editor, manage media, members, languages and translations, and schedule publishing. Developers extend it with C# and Razor views, and the Content Delivery API serves content to any front end. Umbraco 17 is a long-term support (LTS) release on .NET 10.

This guide covers launching and using the Umbraco 17.8.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud BookStack, CMS, Blogs and Wikis listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

What you get:

  • No reachable installer and no shared database. A fresh Umbraco opens its installation wizard to whoever visits first. This image ships no database, no data protection keys and no administrator. Your instance's first boot generates the administrator password and runs Umbraco's own unattended install before the web server starts, as the unprivileged umbraco user on a temporary local port. The installer process is then stopped and checked to be gone, and nginx refuses the install API.
  • Secrets of your instance's own, never on a command line. The administrator password, the ASP.NET Core data protection keys that protect back-office sign-in, the site id, the key that signs image URLs 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 fixed rich-text editor. Umbraco 17.8.0's back office bundles prosemirror-view 1.42.2, which has a high-severity cross-site scripting flaw in its paste handling (GHSA-c8x8-7fp4-3x9w, fixed in 1.42.3). This image rebuilds Umbraco's back office from the official 17.8.0 source with that one library raised to 1.42.3 and nothing else changed. Step 13 shows how to check it.
  • A patched runtime. Umbraco runs on the ASP.NET Core 10.0.14 runtime from Ubuntu's main archive, the October 2026 .NET security release. The site's NuGet packages are restored from a lock file that pins every package by its content hash.
  • HTTPS from the first boot. nginx serves Umbraco on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and the back office, which needs a secure connection for its sign-in, works straight away.
  • Fail-closed startup. nginx and Umbraco 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 the back-office sign-in, two-factor, token, password reset and invitation routes, in every spelling Umbraco accepts. It uses the address of the connection, never a header a client sends. The administrator password is 25 random characters, and Umbraco locks an account for 15 minutes after 5 failed attempts.
  • Requests from other sites cannot act for you. Every request that changes something (any method but GET) and every WebSocket connection must prove that it comes from your site's own address. Anything else is refused before it reaches Umbraco.
  • The real host and client address. nginx hands Umbraco the host name you configure and nothing a request claims, so password reset and invitation links use your host name, and Umbraco's audit log records the address of the connection.
  • Hardened defaults. Umbraco's web server (Kestrel) listens on the loopback interface only, behind nginx. It runs as the umbraco user with systemd's sandboxing, and the application files are read-only to it. Everything Umbraco writes (the database, media, templates, stylesheets, scripts, logs and indexes) lives under /var/lib/umbraco. Umbraco, 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.
  • A 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 (Umbraco) and TCP 80 (redirects to HTTPS) from the networks that should reach them. Keep port 22 open to your own networks only. Open port 443 to your visitors for a public site, or only to your team while you build it.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, and then choose Continue to Configuration. Pick Umbraco 17.8.0 on Ubuntu 24.04 as the software version and pick your Region. Then choose Launch through EC2. Choose m5.large, your key pair and a security group as described in the prerequisites.

Alternatively, launch 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=umbraco}]'
2

Connect to your instance

Version SSH user

Umbraco 17.8.0 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot takes about a minute, because Umbraco's installer creates the database. After that, Umbraco and nginx are active, and the first boot unit is enabled.

systemctl is-active umbraco nginx
systemctl is-enabled umbraco nginx umbraco-firstboot
active active enabled enabled enabled

Only SSH and nginx listen on the network. Umbraco'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 511 0.0.0.0:443 0.0.0.0:* nginx LISTEN 0 511 0.0.0.0:80 0.0.0.0:* nginx LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* sshd LISTEN 0 512 127.0.0.1:5000 0.0.0.0:* dotnet LISTEN 0 511 [::]:443 [::]:* nginx LISTEN 0 511 [::]:80 [::]:* nginx LISTEN 0 4096 [::]:22 [::]:* sshd

Check the runtime and the release.

dotnet --list-runtimes head -1 /usr/share/doc/umbraco/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] Umbraco CMS 17.8.0 (MIT, LICENSE.md at tag release-17.8.0, sha256 e15305002597b35d0afa0ca3a218f2ee7ba171b98be06649d1a2a2fb4069c5f4)
4

Get your sign-in details

The credentials file holds the address Umbraco is served as (your instance's public DNS name by default), the back-office address, and the administrator's email and password. All of these are generated on this instance. Show the file with the following command.

sudo cat /root/umbraco-credentials.txt
UMBRACO_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ UMBRACO_BACKOFFICE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/umbraco UMBRACO_ADMIN_EMAIL=admin@example.com UMBRACO_ADMIN_PASSWORD=<12 letters and digits>-<12 letters and digits>

This is the only back-office user, and it is a member of Umbraco's Administrators group. Its email starts as admin@example.com. Change it to your own address in the top-right user menu (Edit), and configure SMTP (Step 11) before you rely on password reset or invitation emails.

5

Sign in to the back office

Open UMBRACO_BACKOFFICE_URL in a browser. The certificate was generated on your instance, so the browser warns that it does not know the issuer. Compare the certificate's SHA-256 fingerprint with the one in Step 10 and continue, or install your own certificate (Step 10). Sign in with the email address and password from Step 4.

The Content section opens. A new site has no content yet. Create document types and templates in Settings, and then create content in Content.

6

Create and edit content

In Settings > Document Types, create a document type (for example Page) with a Rich Text Editor property. Allow it at the root and let Umbraco create its template. Then, in Content, create a page of that type. The rich-text editor has a formatting toolbar, links, media and an HTML source view. Text you type and text you paste from other documents keeps its formatting.

Choose Save and publish to make the page live at your site's address.

Templates, partial views, stylesheets and scripts you edit in Settings are saved under /var/lib/umbraco (Step 7). Upload images and files in Media, add editors in Users, and add website members in Members.

7

Locate your data

Umbraco keeps everything it writes under /var/lib/umbraco. The application in /opt/umbraco/app is read-only to Umbraco and reaches those folders through links.

sudo ls -la /var/lib/umbraco | sed 1d ls -l /opt/umbraco/app /opt/umbraco/app/wwwroot | grep -- '->' | awk '{print $9, $10, $11}'
drwxr-x--- 9 umbraco umbraco 4096 Nov 1 03:09 . drwxr-xr-x 49 root root 4096 Nov 1 03:04 .. drwxr-x--- 2 umbraco umbraco 4096 Nov 1 03:16 css drwxr-x--- 2 umbraco umbraco 4096 Nov 1 03:16 data drwx------ 2 umbraco umbraco 4096 Nov 1 03:16 dp-keys drwxr-x--- 2 umbraco umbraco 4096 Nov 1 03:16 media drwxr-x--- 2 umbraco umbraco 4096 Nov 1 03:16 scripts drwxr-x--- 4 umbraco umbraco 4096 Nov 1 03:16 umbraco-runtime drwxr-x--- 3 umbraco umbraco 4096 Nov 1 03:19 views Views -> /var/lib/umbraco/views umbraco -> /var/lib/umbraco/umbraco-runtime css -> /var/lib/umbraco/css media -> /var/lib/umbraco/media scripts -> /var/lib/umbraco/scripts
  • data/Umbraco.sqlite.db holds content, document types, users, members and settings (SQLite).
  • dp-keys holds the keys that protect back-office sign-in. Back them up with the database. Without them, every user has to sign in again.
  • media, views, css and scripts hold uploaded media and the templates, partial views, stylesheets and scripts edited in the back office.
  • umbraco-runtime holds Umbraco's logs (Logs), search indexes and generated models (Data/TEMP).
  • /etc/umbraco/appsettings.Instance.json holds this instance's site id, the key that signs image URLs and the application URL. It is root-owned and readable by Umbraco.

For a consistent backup, stop Umbraco, copy /var/lib/umbraco and /etc/umbraco, and start Umbraco again (sudo systemctl stop umbraco, then sudo systemctl start umbraco). Alternatively, take an EBS snapshot of the volume. Umbraco can also use SQL Server instead of SQLite (see Umbraco's documentation), but this image is configured for SQLite.

8

Change settings

Umbraco reads /opt/umbraco/app/appsettings.json, then /opt/umbraco/app/appsettings.Production.json, then /etc/umbraco/appsettings.Instance.json. The second file holds this image's settings: the database, Kestrel on loopback, telemetry at Umbraco's lowest level, keep-alive self-ping off, and the lockout time. Add your own settings to appsettings.Production.json (for example SMTP, Step 11) and restart Umbraco.

sudo nano /opt/umbraco/app/appsettings.Production.json
sudo systemctl restart umbraco

Umbraco reports its version and the site id to telemetry.umbraco.com once a day at the Minimal level. The Content dashboard shows Umbraco's news from news-dashboard.umbraco.com. To change the telemetry level, open Settings > Telemetry Data.

9

Behind a proxy or load balancer

nginx hands Umbraco the configured host name, https and the address of the connection, and it 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 10). Umbraco 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.

API clients that send state-changing requests to the Management API (for example an API user with client credentials) must send the header Origin: https://<your host name>. Requests without proof that they come from your site are refused with 403.

10

Security checks and your own DNS name

Port 80 redirects to HTTPS, and a request for any other host name is dropped (curl reports 000). Each of the following command blocks reads your host name from the credentials file first.

H="$(sudo sed -n 's#^UMBRACO_URL=https://\([^/]*\)/$#\1#p' /root/umbraco-credentials.txt)" curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' "http://${H}/umbraco" 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/umbraco 000

The certificate is an end-entity certificate for your host name, so it cannot sign other certificates. Compare its fingerprint with the one your browser shows.

sudo openssl x509 -in /etc/nginx/ssl/umbraco.crt -noout -subject -ext basicConstraints,extendedKeyUsage -fingerprint -sha256
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Basic Constraints: critical CA:FALSE X509v3 Extended Key Usage: TLS Web Server Authentication sha256 Fingerprint=<your instance's fingerprint>

Kestrel cannot be reached on the instance's own network address, and Umbraco, nginx and first boot are blocked from the instance metadata service. Confirm both.

curl -s -m 5 -o /dev/null -w '%{http_code}\n' "http://$(hostname -I | awk '{print $1}'):5000/" || true systemctl show -p IPAddressDeny umbraco nginx umbraco-firstboot
000 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32

nginx refuses a request from another site that changes something, and one with no proof of where it comes from, with 403. The install API is not served (404).

H="$(sudo sed -n 's#^UMBRACO_URL=https://\([^/]*\)/$#\1#p' /root/umbraco-credentials.txt)" curl -sk --resolve "${H}:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{}' "https://${H}/umbraco/management/api/v1/security/back-office/login" curl -sk --resolve "${H}:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' -H 'Content-Type: application/json' -d '{}' "https://${H}/umbraco/management/api/v1/security/back-office/login" curl -sk --resolve "${H}:443:127.0.0.1" -o /dev/null -w '%{http_code}\n' "https://${H}/umbraco/management/api/v1/install/settings"
403 403 404

The sign-in limit applies per network address. Loopback traffic is not limited, so test from the instance's own network address. The first attempts reach Umbraco (401: wrong credentials), and the rest are refused with 429.

H="$(sudo sed -n 's#^UMBRACO_URL=https://\([^/]*\)/$#\1#p' /root/umbraco-credentials.txt)" P="$(hostname -I | awk '{print $1}')" for i in $(seq 1 14); do curl -sk -o /dev/null -w '%{http_code} ' --interface "${P}" --resolve "${H}:443:${P}" -H "Origin: https://${H}" -H 'Content-Type: application/json' -d '{"username":"nobody@example.com","password":"x"}' "https://${H}/umbraco/management/api/v1/security/back-office/login"; done; echo
401 401 401 401 401 401 401 401 401 401 401 429 429 429

Use your own DNS name. Point a DNS name at the instance's public address (an Elastic IP keeps it stable), and then make it the name Umbraco is served as. The helper mints a new certificate for the name, pins nginx and the request guards to it, sets Umbraco's application URL and restarts Umbraco. The instance's IP addresses then redirect to the name.

sudo umbraco-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/umbraco.crt and /etc/nginx/ssl/umbraco.key with your full chain and key, 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/sac-umbraco-headers.conf. It is left out while the certificate is self-signed.

11

Manage passwords, email, upgrades and packages

  • Password: Change the administrator password in the top-right user menu, and keep /root/umbraco-credentials.txt up to date or delete it. Add a user for each editor in Users rather than sharing the administrator.
  • Email: Add an Umbraco:CMS:Global:Smtp section (host, port, sender address and credentials) to appsettings.Production.json (Step 8). Umbraco sends password reset, invitation and notification email through it.
  • Upgrades and packages: Umbraco and its packages are NuGet packages compiled into the site, so an upgrade is a rebuild of the site. SecureAnyCloud publishes new versions of this image for Umbraco and .NET security releases. To move an existing site, back it up (Step 7), launch the new version, and restore /var/lib/umbraco and /etc/umbraco into it. Umbraco upgrades the database on start. Install only packages you trust, because a package is .NET code that runs inside Umbraco.
  • Logs: Settings > Log Viewer shows Umbraco's own log. The log files are in /var/lib/umbraco/umbraco-runtime/Logs, and the service log is available with sudo journalctl -u umbraco.
12

Server components

dpkg-query -W -f='${Package} ${Version}\n' aspnetcore-runtime-10.0 nginx sqlite3
aspnetcore-runtime-10.0 10.0.14-0ubuntu1~24.04.1 nginx 1.24.0-2ubuntu7.19 sqlite3 3.45.1-1ubuntu2.8

Component Where

Umbraco 17.8.0 /opt/umbraco/app (read-only), service umbraco (Kestrel on 127.0.0.1:5000)

nginx /etc/nginx/sites-available/sac-umbraco, TLS in /etc/nginx/ssl

Data, keys and edited files /var/lib/umbraco

Instance settings /etc/umbraco/appsettings.Instance.json

First boot umbraco-firstboot.service, /usr/local/sbin/umbraco-firstboot.sh

Helpers /usr/local/sbin/umbraco-set-hostname, /usr/local/sbin/umbraco-cred-roundtrip

13

The rebuilt back office

Umbraco 17.8.0's published back office bundles prosemirror-view 1.42.2, the library under its Tiptap rich-text editor. That version runs pasted HTML through less validation than it should (GHSA-c8x8-7fp4-3x9w, CVE-2026-104847, high severity, fixed in 1.42.3), and no Umbraco release ships the fix yet.

This image builds the back office from Umbraco's own release-17.8.0 source with one change: an npm override that raises prosemirror-view to 1.42.3 (MIT license), so the lock file differs in that one package. The build is reproducible, and only the rich-text editor's files differ from Umbraco's published back office. The details are recorded on the instance.

grep -A2 '^Back office' /usr/share/doc/umbraco/RELEASE
grep -rlF '"ProseMirrorClipboard"' /opt/umbraco/app/wwwroot/umbraco/backoffice --include='*.js' | sed 's#.*/backoffice/#backoffice/#'
Back office (d-742): src/Umbraco.Web.UI.Client of release-17.8.0 (commit 83984e2515634f5812d9af99e0149cd7d94622c8, tarball sha256 1b737566fd107b9c0e66c88fc82136f5468c29454c20596d2165d462684b1ce5) rebuilt with ONE npm override, prosemirror-view 1.42.2 -> 1.42.3 (GHSA-c8x8-7fp4-3x9w / CVE-2026-104847, paste XSS; MIT). Node.js v24.22.2 (sha256 48cc0e21e048790e54c0cf5749c2725005313d652651c40b7a21feb9697aaf75), npm ci + npm run build:for:cms. Output: 2459 files, digest 0b4e3bc2915b8f63c483714476675bdea67f492d7c1dacb8802c023b81205497 (reproduced on the build workstation three times and on this builder). backoffice/packages/tiptap/tiptap-toolbar-element-api-base-705jTA9z.js

When an Umbraco release ships prosemirror-view 1.42.3 or later, SecureAnyCloud's next version of this image uses Umbraco's own back office again.

14

Security recommendations

  • Allow port 22 in the security group from your own networks only.
  • Change the administrator's email to your own (Step 4), keep its password secret, and change the password if it may have been shared (Step 11). Give each editor their own user with only the sections they need.
  • 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 10).
  • Keep Umbraco on the newest release of the 17 LTS line (Step 11), and install only packages you trust.
  • Keep the operating system patched. Unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else, including the .NET runtime.

Support and licensing

Umbraco CMS is free software under the MIT License (a copy is at /usr/share/doc/umbraco/LICENSE.md). prosemirror-view is under the MIT License. ImageSharp is under the Six Labors Split License, which grants the Apache License 2.0 when it is used as a dependency of open-source software such as Umbraco. The other bundled libraries carry their own open-source licenses.

There is no per-user or per-CPU license fee and no license key. Umbraco's commercial add-ons (Forms, Deploy, Workflow) are not installed. This image builds the upstream release with the one library raise described in Step 13, plus the SecureAnyCloud first boot, settings and helpers described above. The AMI price covers SecureAnyCloud's hardening, first boot setup and support. SecureAnyCloud is not affiliated with or endorsed by Umbraco A/S or the .NET Foundation.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24-hour response SLA

Deploy on AWS

Launch Umbraco 17.8.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS