Umbraco CMS on AWS User Guide
Overview
Umbraco is an open source content management system built on ASP.NET Core. This image runs Umbraco 17.5.3, the current patch of the Umbraco 17 long term support release, on the .NET 10 runtime.
The application server (Kestrel) is bound to 127.0.0.1 and is never reachable from the network. nginx is the only network listener: it serves the public website and an unauthenticated health endpoint on port 80, and the back office over HTTPS on port 443.
Content is stored in an embedded SQLite database, so there is no separate database server to license, patch or operate. That suits a single instance site. If you need a clustered or externally hosted database, Umbraco also supports Microsoft SQL Server and the connection string can be repointed.
The image ships with no database at all. On the first boot of every instance a one shot service creates the database from scratch and generates an administrator password, an ASP.NET Core Data Protection key ring and a TLS key that are unique to that instance. Two instances launched from this image never share any of them.
What is in the image
| Component | Detail |
|---|---|
| Umbraco CMS | 17.5.3 (Umbraco 17 long term support line, MIT licensed) |
| Runtime | .NET 10 / ASP.NET Core 10, installed from the Ubuntu archive |
| Web server | nginx, port 80 (public site) and port 443 (back office) |
| Application server | Kestrel on 127.0.0.1:5000, loopback only |
| Database | SQLite, embedded, on a dedicated data volume |
| Health endpoint | http://<public-ip>/healthz, unauthenticated, served by nginx |
Prerequisites
Before you deploy this image you need:
- An AWS account with permission to launch EC2 instances.
- A security group allowing inbound TCP 22 from your administration address, and TCP 80 and 443 from wherever your site's visitors and editors will connect.
- An EC2 key pair, so you can read the generated administrator password over SSH.
- An instance type of at least
m5.large. Umbraco compiles Razor views and builds its cache on first request, so smaller burstable types make the first page load noticeably slower.
Connecting to your instance
| OS variant | SSH login user | Command |
|---|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i /path/to/key.pem ubuntu@<public-ip> |
The ubuntu user has passwordless sudo. There is no other login account on the image, and no account carries a password.
First boot
First boot takes roughly two to three minutes. During that time the instance:
- Reads its own public IPv4 address from EC2 IMDSv2, so the back office sign in flow is registered against the address you will actually browse to.
- Generates a random administrator password.
- Creates the SQLite database and the administrator account.
- Generates a fresh ASP.NET Core Data Protection key ring, which signs and encrypts the back office authentication cookie.
- Generates a self signed TLS certificate and key for the HTTPS listener.
- Starts nginx and writes the credentials file.
Check that both services are up:
systemctl is-active umbraco.service nginx.service
Confirm the runtime version:
dotnet --list-runtimes
Confirm the health endpoint answers. This endpoint needs no credential, so it is what you point a load balancer health check at:
curl -s http://127.0.0.1/healthz
Retrieving your administrator password
The credentials file is mode 0600 and readable only by root. Its keys are:
sudo grep -oE '^[A-Z_]+=' /root/umbraco-credentials.txt
To read the password itself, run the command below on your instance. It is deliberately not reproduced in this guide, because the value is unique to your instance:
| What you want | Command to run on the instance |
|---|---|
| The whole credentials file | sudo cat /root/umbraco-credentials.txt |
| Just the administrator email | sudo grep '^UMBRACO_ADMIN_USER=' /root/umbraco-credentials.txt \| cut -d= -f2- |
| Just the password | sudo grep '^UMBRACO_ADMIN_PASSWORD=' /root/umbraco-credentials.txt \| cut -d= -f2- |
Signing in to the back office
Open https://<public-ip>/umbraco in a browser and sign in with the email address and password from the credentials file.
Two things are worth knowing:
- The back office is HTTPS only, and that is deliberate. Umbraco 17's back office is a single page application whose sign in uses
window.crypto.subtle, which browsers expose only in a secure context. Over plain HTTP the sign in cannot complete. Port 80 therefore redirects/umbracoto HTTPS and serves the public website. - Your browser will warn about the certificate once. The image generates a self signed certificate per instance rather than shipping a shared one. Accept the warning to sign in, then replace the certificate as described below before you put the site in front of the public.
The public website is served at http://<public-ip>/. Until you publish your first page it shows Umbraco's "Welcome to your Umbraco installation" holding page.
Verifying the sign in works and a wrong password is rejected
The image ships a self check that exercises the real sign in path through nginx on port 443. It proves that your instance's password is accepted, and that a wrong password is rejected with an explicit HTTP 401 rather than merely failing to succeed. It prints no secret:
sudo /usr/local/sbin/umbraco-cred-roundtrip.sh
Confirming this instance's secrets are its own
Every piece of secret material is generated per instance. This command prints SHA-256 digests of that material, never the material itself, so you can compare two instances and confirm they differ:
sudo /usr/local/sbin/umbraco-instance-fingerprint.sh
Run it on two instances launched from the same image and all four digests will differ. The Data Protection key ring matters most: it is the key that signs the back office authentication cookie, so if it were shared, a cookie minted on one instance would be accepted by another.
Storage layout
Two dedicated EBS volumes are attached and mounted, separate from the operating system disk:
df -h --output=source,size,used,avail,target /var/lib/umbraco /var/lib/umbraco-media
Both are recorded in /etc/fstab by filesystem UUID, so the layout reproduces on every instance launched from this image:
grep umbraco /etc/fstab
| Path | Holds |
|---|---|
/var/lib/umbraco |
SQLite content database, ASP.NET Data Protection key ring, Umbraco runtime cache and logs |
/var/lib/umbraco-media |
The media library: every image, document and asset uploaded through the back office |
/opt/umbraco/app |
The published application, on the OS disk, root owned and read only to the service account |
Media is separated because it is the tier that grows without bound on a content managed site. You can grow either volume with an EBS volume modification followed by sudo resize2fs <device> without touching the other.
Network exposure
The application server is bound to loopback and is not reachable from outside the instance. Only nginx listens on a routable address:
sudo ss -tlnp | grep -E 'dotnet|nginx'
dotnet appears only on 127.0.0.1:5000. If you ever see it on 0.0.0.0, something has changed the ASPNETCORE_URLS setting in /var/lib/umbraco/umbraco.env and you should put it back.
Replacing the self signed certificate
For production, replace the generated certificate with one for your own domain. The simplest approach on AWS is to terminate TLS at an Application Load Balancer using an AWS Certificate Manager certificate and forward to the instance.
To install a certificate directly on the instance instead, place your certificate and key at /etc/nginx/ssl/umbraco.crt and /etc/nginx/ssl/umbraco.key (key mode 0600), then run sudo nginx -t && sudo systemctl reload nginx.
If you move the site to a custom domain, also update Umbraco__CMS__WebRouting__UmbracoApplicationUrl in /var/lib/umbraco/umbraco.env to that domain and restart umbraco.service. That value seeds the back office sign in flow's permitted redirect addresses, so sign in fails on a host it does not cover.
Licensing
Umbraco CMS is open source software under the MIT licence. The upstream licence text is included on the image:
head -3 /usr/local/share/cloudimg-umbraco/LICENSE.md
The exact Umbraco version this image resolved and published:
python3 -c "import json;d=json.load(open('/opt/umbraco/app/Cloudimg.Umbraco.deps.json'));print(sorted(k for k in d['libraries'] if k.startswith('Umbraco.Cms/'))[0])"
This image contains only the MIT licensed Umbraco CMS. Umbraco Cloud and Umbraco's commercial add on products are not included. cloudimg is not affiliated with, endorsed by or sponsored by Umbraco HQ; the Umbraco name is used only to identify the open source software included here.
Your cloudimg charges are for the packaging, hardening and support around the software, not for the software itself.
Backup and maintenance
- Backup. The content database and the media library both live on the two data volumes, so an AWS Backup plan or scheduled EBS snapshots of those volumes captures the whole site. Snapshot both together for a consistent restore.
- Operating system updates.
unattended-upgradesis enabled. Because the .NET runtime is installed from the Ubuntu archive rather than a third party feed, ASP.NET Core security updates arrive on the same stream as the rest of the operating system. - Umbraco upgrades. Umbraco 17 is a long term support release supported until 27 November 2028. Take a snapshot of both data volumes before any upgrade.
- Logs. Application logs are under
/var/lib/umbraco/umbraco-runtime/Logs. Service logs are in the journal:sudo journalctl -u umbraco.service.
Screenshots
The Umbraco back office sign in page:

The Settings section, where document types, templates and data types are modelled:

The public website immediately after launch, before any content is published:

The media library, stored on its own dedicated volume:

Support
cloudimg provides 24/7 technical support by email and live chat for this image, covering launch, configuration, upgrades and operational questions. We aim to respond within one business day.
- Email: support@cloudimg.co.uk
- Web: www.cloudimg.co.uk