Deployment guide

Rauthy 0.37.2

Overview

Rauthy is an open source OpenID Connect (OIDC) provider and single sign-on server. Your applications send their users to Rauthy to sign in with a password, a passkey or both. Rauthy then returns an ID token and an access token signed with this instance's keys. It manages clients, users, groups, roles and scopes, can federate sign-in to an upstream identity provider, and includes an admin console and a self-service account page.

This guide covers the Rauthy 0.37.2 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud identity, secrets and PKI listing on AWS Marketplace, and you select it as the software version when you subscribe. The image is a hardened, ready-to-use Rauthy server.

What you get:

  • nginx in front, Rauthy behind it. nginx is the only public listener, on ports 80 and 443. Rauthy listens on 127.0.0.1:8080, and its embedded database, Hiqlite (SQLite replicated with Raft), listens on 127.0.0.1:8100 and 127.0.0.1:8200. All three are loopback only.
  • HTTPS from the first boot. First boot names the instance after its EC2 public DNS name and serves HTTPS with a certificate generated on your instance for that name, plus the public and private addresses. The OIDC issuer is https://<that name>/auth/v1/. Port 80 only redirects to HTTPS and serves the Let's Encrypt challenge path. A single command moves Rauthy to your own domain and certificate.
  • Secrets unique to your instance. The image ships with no configuration secret, no database, no admin and no key. First boot generates this instance's Hiqlite Raft and API secrets, and the AES-256 key that encrypts every client secret, MFA secret and signing key in the database. Rauthy's first start then creates the database and this instance's token signing keys (JWKS). The single admin account gets a random password, which Rauthy stores only as an Argon2id hash.
  • The admin console requires a passkey. The admin signs in with the password and registers a passkey on the account page. From then on, the admin console asks for both the password and the passkey (Rauthy's admin_force_mfa, on). Open self-registration is off, so nobody can create an account without the admin.
  • The real client address, everywhere. Rauthy trusts only the local nginx as a proxy (proxy_mode, trusted_proxies = ['127.0.0.1/32']). nginx passes it the address of the TCP connection, never a header that a client could forge. Rauthy's failed-login counter, its sessions and its logs all see the real client.
  • Sign-in limits per client. nginx limits each client address on the sign-in route (30 a minute, bursts of 30), on the token endpoint (10 a second, bursts of 50) and on everything else (20 a second, bursts of 100). The web interface's own files are never limited.
  • Outbound connections cannot reach your private network. Rauthy connects out by itself when an admin adds an upstream identity provider (its discovery document and JWKS), for e-mail and for webhooks. Below the application, a firewall table refuses the instance metadata service and every private, link-local, shared and reserved address to the Rauthy account, including this VPC's own ranges, unless you open a host.
  • Browser hardening. Content-Security-Policy with frame-ancestors 'none', X-Frame-Options: DENY, Strict-Transport-Security, X-Content-Type-Options, Referrer-Policy and Permissions-Policy. The access log records paths only, never a query string, so an authorization code never reaches a log.
  • Locked down. TLS 1.2 and 1.3 only. The AMI requires IMDSv2, and Rauthy and nginx are blocked from the instance metadata service. Rauthy runs as its own account with NoNewPrivileges, core dumps off (LimitCORE=0) and info-level logging. No mail server is installed, and nothing listens on port 25.
  • Pinned and verified. The image is built on a fully patched Ubuntu 24.04 LTS, with unattended security upgrades switched on. Step 13 shows exactly what is installed.

What is on the image: Rauthy 0.37.2, which is upstream's own release build. It is the single binary from the official ghcr.io/sebadob/rauthy:0.37.2 image, pinned by digest, in /usr/local/bin/rauthy, and no container runtime is installed. The image also contains nginx and nftables from Ubuntu 24.04, cloud-init and the AWS Systems Manager agent.

Settings that differ from Rauthy's defaults: Rauthy listens on 127.0.0.1 instead of 0.0.0.0. proxy_mode is on and trusts only 127.0.0.1/32. Hiqlite's API and Raft ports listen on 127.0.0.1 instead of 0.0.0.0. Rauthy's check for new versions, which makes a call to GitHub, is off.

Settings pinned at Rauthy's own defaults: admin_force_mfa = true, open registration off, ATProto sign-in off, the Prometheus metrics listener off and the Swagger UI off.

Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.

Prerequisites

Before you launch the instance, make sure you have:

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet that gives the instance a public IPv4 address. Alternatively, reach the instance over your VPN or Direct Connect.
  • A security group that allows TCP 22 (SSH) from your own networks only. It must also allow TCP 443 (Rauthy) and TCP 80 (redirects to HTTPS) from the networks whose users and applications sign in with Rauthy. To get a certificate from Let's Encrypt, port 80 must be reachable from the internet while the request runs.
  • A browser with a passkey, such as a phone, a security key, or the passkey store of your computer or password manager.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, and then choose Continue to Configuration. Select Rauthy 0.37.2 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2. Choose m5.large, your key pair and a security group that meets the prerequisites.

To launch with the AWS CLI instead, replace the values in angle brackets:

aws ec2 run-instances --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> \
  --associate-public-ip-address \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=rauthy}]'

First boot takes about a minute after the instance is running. It generates the secrets and the certificate, and it starts Rauthy on its empty database, which creates the signing keys and the admin account. It then proves the admin sign-in on the loopback address, and only then opens the web port.

2

Connect to your instance

Connect over SSH as the ubuntu user, the default user on Ubuntu 24.04:

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

The login message shows the address of Rauthy and the commands that this guide uses.

3

Check the services

systemctl is-active rauthy nginx sac-rauthy-metadata
sudo ss -tln | grep -E ':(22|25|80|443|8080|8100|8200) '
sudo rauthy-selftest

All three services report active. nginx is the only service on ports 80 and 443. Rauthy (8080) and its Hiqlite database (8100 and 8200) listen on 127.0.0.1 only, and nothing listens on port 25:

active active active LISTEN 0 1024 127.0.0.1:8100 0.0.0.0:* LISTEN 0 1024 127.0.0.1:8080 0.0.0.0:* LISTEN 0 511 0.0.0.0:443 0.0.0.0:* LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 1024 127.0.0.1:8200 0.0.0.0:* LISTEN 0 511 [::]:443 [::]:* LISTEN 0 511 [::]:80 [::]:* LISTEN 0 4096 [::]:22 [::]:* rauthy-selftest: issuer https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/, 4 JWKS keys; wrong password 401; issuer=https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/ alg=EdDSA kid=Xk3pQ9vT2mLs8RwY1cJd4HnB sig=verified(tampered refused) aud=rauthy nonce=ok userinfo=200 code_replay=401 admin_api=406/MfaRequired; stranger registration 403; 4 admin endpoints 401 anonymous RAUTHY_SELFTEST_OK

rauthy-selftest signs in with a throwaway client and user over the public HTTPS name and checks the ID token's signature against the JWKS. It confirms that a tampered token, a replayed code and a wrong password are refused, that the admin API requires MFA, and that a stranger cannot register. It then deletes the client and the user again.

4

Read the credentials file and the certificate fingerprint

sudo cat /root/rauthy-credentials.txt

The file is readable by root only (mode 0600). It holds this instance's addresses, the SHA-256 fingerprint of its certificate, and the admin's sign-in name and password:

# Rauthy 0.37.2 on this instance (sac). Sign in at RAUTHY_ACCOUNT_URL as RAUTHY_ADMIN_EMAIL with
# RAUTHY_ADMIN_PASSWORD, then register a passkey (MFA): the admin console (RAUTHY_ADMIN_URL) requires one.
# The certificate is self-signed for this instance: compare its SHA-256 fingerprint with RAUTHY_TLS_SHA256.
# Change the password and the e-mail address in the account page after the first sign-in. RAUTHY_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/ RAUTHY_ACCOUNT_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/account RAUTHY_ADMIN_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/admin RAUTHY_ISSUER=https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/ RAUTHY_HOST=ec2-203-0-113-10.compute-1.amazonaws.com RAUTHY_PASSKEY_RP_ID=ec2-203-0-113-10.compute-1.amazonaws.com RAUTHY_ADMIN_EMAIL=admin@rauthy.invalid RAUTHY_TLS_SHA256=4F:2A:91:...:7D:0C (32 hex pairs) RAUTHY_NAME_SOURCE=public-dns-name RAUTHY_ADDRESS=203.0.113.10 RAUTHY_ADDRESS_SOURCE=metadata-public-address GUIDE=https://secureanycloud.com/stacks/rauthy-0-37-2/ RAUTHY_ADMIN_PASSWORD=<a random 28-character password>

The certificate is self-signed for this instance. Before you type the password into a browser, check the certificate fingerprint. Click the padlock or the warning, open the certificate details, and confirm that the fingerprint matches the RAUTHY_TLS_SHA256 value in this file. To check from your own computer instead, run:

openssl s_client -connect <public-ip>:443 -servername <public-dns-name> </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

The password in this file is the one that first boot set. After you change it on the account page (Step 5), the file no longer holds the current password, because Rauthy keeps only its hash.

5

Sign in and register your passkey

Open RAUTHY_ACCOUNT_URL (https://<public-dns-name>/auth/v1/account) and accept the certificate once you have checked its fingerprint. On the Rauthy sign-in page, sign in with admin@rauthy.invalid and RAUTHY_ADMIN_PASSWORD. Rauthy refuses a wrong password and counts failed sign-ins per client address.

After you sign in, you land on your account page. Open MFA and select Register New Passkey. Rauthy asks for your current password once more, and then for a name for the passkey. Select Register, and your browser asks your authenticator (a phone, a security key or the passkey store of your computer) to create a passkey for this site.

While you are on the account page, change the password, and change the e-mail address to one that you read.

Passkeys are bound to the name in RAUTHY_PASSKEY_RP_ID. If you later move Rauthy to your own domain (Step 8), register the passkey again after the move, and do so before you rely on it.

6

Open the admin console

Open RAUTHY_ADMIN_URL (https://<public-dns-name>/auth/v1/admin). Rauthy asks for the password and then for your passkey, and then opens the admin console. A fresh instance has the admin account, the built-in rauthy client (the console and account page themselves) and Rauthy's default groups, roles and scopes.

Without a passkey on the account, the admin console stays closed and the admin API answers MfaRequired.

7

Connect your first application

In the admin console, open Clients and add a client for your application. Enter its client ID and its redirect URI (for example https://app.example.com/oauth2/callback), and choose whether it is confidential or public. A confidential client is a server-side application and gets a client secret. A public client is a browser or mobile application and must use PKCE.

Then add users under Users, or let your users sign in through an upstream provider that you configure under Providers.

Your application needs only the issuer. Its discovery document lists every endpoint and the JWKS that verifies Rauthy's tokens:

H="$(sudo sed -n 's/^RAUTHY_HOST=//p' /root/rauthy-credentials.txt)"
sudo curl -s --cacert /etc/ssl/rauthy/rauthy.crt "https://$H/auth/v1/.well-known/openid-configuration" \
  | jq '{issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri, id_token_signing_alg_values_supported}'
sudo curl -s --cacert /etc/ssl/rauthy/rauthy.crt "https://$H/auth/v1/oidc/certs" | jq -c '.keys[] | {kid, alg, kty}'
{ "issuer": "https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/", "authorization_endpoint": "https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/oidc/authorize", "token_endpoint": "https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/oidc/token", "userinfo_endpoint": "https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/oidc/userinfo", "jwks_uri": "https://ec2-203-0-113-10.compute-1.amazonaws.com/auth/v1/oidc/certs", "id_token_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "EdDSA", "Ed25519" ] } {"kid":"Qm7RtV2nWc9LpX4aKd8sHy1E","alg":"RS256","kty":"RSA"} {"kid":"Ub5JzN0eFq3TgY6wMr2vPa9C","alg":"RS384","kty":"RSA"} {"kid":"Dh8KsL1xBn4ViR7oTe5yWq0G","alg":"RS512","kty":"RSA"} {"kid":"Xk3pQ9vT2mLs8RwY1cJd4HnB","alg":"EdDSA","kty":"OKP"}

The commands use sudo only because the certificate under /etc/ssl/rauthy is readable by root. From your applications, use the issuer URL with the certificate that you trust.

The signing keys belong to this instance. Rauthy rotates them monthly and keeps publishing the previous keys, so tokens that were already issued keep verifying.

8

Your domain and a trusted certificate

  • Create a DNS record for your name, for example an A record that points id.example.com to the instance's Elastic IP address.
  • Point Rauthy at the name. The first name becomes the issuer's host, the passkey relying party, the name that nginx serves and the name that the certificate covers. Any names after it keep working too:
sudo rauthy-set-domain id.example.com "$(sudo sed -n 's/^RAUTHY_HOST=//p' /root/rauthy-credentials.txt)"
  • Get a certificate for the name. With Let's Encrypt, nginx already serves the HTTP challenge directory, so certbot's webroot mode works without stopping anything. Port 80 must be reachable from the internet while it runs:
sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/sac-acme -d id.example.com --email you@example.com --agree-tos -n \
  --deploy-hook 'rauthy-set-domain --tls-cert /etc/letsencrypt/live/id.example.com/fullchain.pem --tls-key /etc/letsencrypt/live/id.example.com/privkey.pem id.example.com'

The deploy hook installs the certificate now and again after every automatic renewal.

If you use a certificate from another authority, run sudo rauthy-set-domain --tls-cert <fullchain.pem> --tls-key <private-key.pem> id.example.com yourself. The command checks that the key matches and that the certificate covers the name. Without --tls-cert, the instance generates a new self-signed certificate for the name.

The issuer changes with the name (https://id.example.com/auth/v1/). Update it in your applications, and register your passkey again (Step 5). nginx answers only the names that you configured, so a request for any other host name, including one forged in the Host header, gets no answer.

9

Sign-in limits and client addresses

nginx limits each client address. A client that passes the limit gets HTTP 429 until its allowance refills, while other clients carry on:

grep -E '^limit_req' /etc/nginx/conf.d/sac-rauthy.conf
limit_req_zone $ra_login_key zone=ra_login:10m rate=30r/m; limit_req_zone $sac_client_key zone=ra_token:10m rate=10r/s; limit_req_zone $sac_client_key zone=ra_all:10m rate=20r/s; limit_req_zone $sac_client_key zone=ra_static:10m rate=100r/s; limit_req_status 429;

The limits are:

  • Sign-in requests (ra_login): 30 a minute, with a burst of 30
  • Token requests: 10 a second, with a burst of 50
  • The web interface's static files: 100 a second, with a burst of 600
  • Everything else: 20 a second, with a burst of 100

Each limit applies per IPv4 address or per IPv6 /64, not per account. Rauthy also slows down repeated failed sign-ins from one address by itself. To tune the limits for your users, edit /etc/nginx/conf.d/sac-rauthy.conf (the zones) and /etc/nginx/sites-available/rauthy (the bursts), and then run sudo nginx -t && sudo systemctl reload nginx.

The limits, Rauthy's failed-login counter, its session records and its logs all use the address of the TCP connection that reached nginx. If you put a load balancer in front of Rauthy, every client then appears with the load balancer's address. To keep the limits per client, use a Network Load Balancer with client IP preservation, or adjust the nginx configuration to suit your load balancer.

10

Outbound connections

Rauthy connects out when an admin adds an upstream identity provider (it fetches the provider's discovery document and keys), when it sends e-mail, and for webhooks. Internet hosts work as they are. The Rauthy account is refused access to the instance metadata service; to private, link-local, shared and reserved ranges; to every IPv6 address except public ones; and to this VPC's own ranges. An admin, or anyone who can make Rauthy fetch a URL, therefore cannot use Rauthy to reach the inside of your network.

Rauthy makes no connection until an admin configures one. With nothing configured, it sends nothing off the instance.

To let Rauthy reach a private host, for example an identity provider on your private network or your SMTP relay, list the host in /etc/sac/rauthy-egress-allow, one address or CIDR per line, and restart. IPv4 ranges can be no wider than /16, and the metadata service can never be opened:

echo 10.0.1.25 | sudo tee -a /etc/sac/rauthy-egress-allow
sudo systemctl restart sac-rauthy-metadata rauthy

The instance reads this VPC's ranges from the instance metadata service at every boot. It keeps the last good list for a boot at which the metadata service does not answer. To see the ranges, run:

cat /run/sac-rauthy/vpc-cidrs
sudo nft list table inet sac_rauthy | grep -E 'set (vpc4|allow4)' -A 4
# written by rauthy-metadata at 2026-11-09T15:34:00Z source imds 4 172.31.0.0/16 set allow4 { type ipv4_addr flags interval } -- set vpc4 { type ipv4_addr flags interval elements = { 172.31.0.0/16 } }

vpc4 holds this VPC's ranges, here the default VPC's 172.31.0.0/16. allow4 holds the addresses that you listed in /etc/sac/rauthy-egress-allow, and it is empty until you add one.

11

Keys, the database and backups

/etc/rauthy/config.toml is Rauthy's configuration, and it holds no secret. /etc/rauthy/secrets.toml holds this instance's Hiqlite Raft and API secrets and the encryption key. Only root and the rauthy service account can read these files:

sudo stat -c '%a %U:%G %n' /etc/rauthy /etc/rauthy/config.toml /etc/rauthy/secrets.toml /var/lib/rauthy /var/lib/rauthy/data
750 root:rauthy /etc/rauthy 640 root:rauthy /etc/rauthy/config.toml 640 root:rauthy /etc/rauthy/secrets.toml 700 rauthy:rauthy /var/lib/rauthy 700 rauthy:rauthy /var/lib/rauthy/data

The Hiqlite database is under /var/lib/rauthy/data. The image ships it empty. First boot creates this instance's database, admin, signing keys and secrets, so no two instances share any of them.

Back up secrets.toml with the database, and protect both. Without the encryption key, the client secrets, MFA secrets and signing keys in a database backup cannot be used. With the key, they can.

Rauthy writes its own database backup every day at 02:30 UTC to /var/lib/rauthy/data/state_machine/backups and keeps three days of backups on this instance's disk. To copy a backup and the keys off the instance, replace <backup-dir> with a directory that only root can read:

sudo install -d -m 0700 <backup-dir>
sudo cp -p /etc/rauthy/config.toml /etc/rauthy/secrets.toml <backup-dir>/
sudo cp -a /var/lib/rauthy/data/state_machine/backups <backup-dir>/
12

Security notes

  • Metadata service and private addresses. The AMI requires IMDSv2, and systemd denies Rauthy, nginx and the first-boot service access to the instance metadata addresses. One small root service, sac-rauthy-metadata, reads this VPC's ranges from the metadata service at every boot and loads them into the firewall table that applies to Rauthy and nginx. If it cannot, Rauthy does not start until it can, and it retries by itself.
  • Where the secrets live. The Hiqlite secrets and the encryption key are stored only in secrets.toml (mode 0640, root:rauthy). They appear in no environment variable, command line, journal or log, and all of them are generated on your instance at first boot. The admin's password reaches Rauthy once, on its first start, and first boot then removes it from the configuration. The instance has no swap, and core dumps are off.
  • Accounts. First boot creates the single admin account, admin@rauthy.invalid, before the web port opens. Open self-registration is off, and the admin console requires a passkey.
  • Response headers. Content-Security-Policy with frame-ancestors 'none' and object-src 'none', X-Frame-Options: DENY, Strict-Transport-Security: max-age=31536000, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer and a Permissions-Policy that turns off the camera, microphone, geolocation, payment and USB APIs.
  • Signing algorithms. Rauthy signs each client's tokens with the algorithm set on that client: EdDSA or RSA (RS256, RS384, RS512). The RSA library that Rauthy uses has a published timing advisory (RUSTSEC-2023-0071, "Marvin") without a fixed release, so prefer EdDSA for clients that support it.
  • Upgrades. Ubuntu security updates, including nginx, install automatically through unattended-upgrades. Rauthy is a pinned release, so SecureAnyCloud publishes new versions of this AMI for new Rauthy releases.
13

Licences and source

Rauthy is licensed under the Apache License 2.0. The image runs upstream's own release binary, unmodified, and records exactly what it was built from. To see the record, run:

cat /usr/share/doc/sac-rauthy/SOURCE.txt
Rauthy 0.37.2 (Apache-2.0), unmodified: the release binary /app/rauthy of the official image ghcr.io/sebadob/rauthy:0.37.2 image index sha256:d733b7580f53c1601c286b5504853b2774e70245f5f9427d9bcda32496b753ee linux/amd64 sha256:666e9f54b4deb99d710448b32eb2c18214160744dd3f68f8f7cee140a6cd80ef (config sha256:58c3ffaacc5ae223b036d5bf08a5d98a41b4bacb8c39cf45bc617433569ecec5) binary layer sha256:95cf26fe5fa83a21f27e5a984b30a2f8f194234c7eed8b9e075c2b5296f5cb0c /usr/local/bin/rauthy sha256 50f65249a718c84c4eb45e8e15156df8ad07ef466867682844463cc83d4dfcc4 (71371040 bytes) Source: the release tag v0.37.2 = commit fe6bcb95192e8a83c29e2b525bf4960933bea3bd https://github.com/sebadob/rauthy/tree/v0.37.2 (source tarball sha256 7623784ebaf5db642af063b7837a2341c002d595b9a54fd445e0dd556de72226) Third-party licences: the crates in Cargo.lock and the frontend's npm modules at that tag (all permissive or MPL-2.0; sac's build census found no CCL, BUSL, SSPL, Elastic or Commons Clause licence and none undeclared). sac changes NO Rauthy source file. The appliance configuration (config template, nginx, systemd units, first-boot helpers) is in /usr/local/lib/sac/rauthy, /usr/local/sbin/rauthy-* and /etc/nginx.

The licence text is in /usr/share/doc/sac-rauthy/LICENSE-rauthy-Apache-2.0.txt. nginx and nftables are the Ubuntu packages, with their licences in /usr/share/doc/<package>/copyright.

Support

SecureAnyCloud supports this image. To reach the support team, email sales@secureanycloud.com. For the other versions in this listing, see the identity, secrets and PKI listing guide.

Deploy on AWS

Launch Rauthy 0.37.2 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