Tinyauth on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Tinyauth 5.1.3 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Tinyauth is a lightweight open source authentication gateway — the simplest way to put a login screen in front of the self-hosted apps you run behind a reverse proxy. Instead of every app needing its own accounts, you place Tinyauth in front of them: a protected route asks Tinyauth whether the visitor is signed in, and if not they are sent to a single, clean login portal. Once authenticated they are allowed through, and the same session covers every app Tinyauth protects.
The image installs the official Tinyauth v5.1.3 release binary (a single, statically-linked Go binary) verified by SHA-256 at install time, and ships the AGPL-3.0 licence text alongside it. Local users are stored as username and bcrypt-hash pairs; Tinyauth also supports OAuth and OIDC providers, LDAP, TOTP two-factor and per-app access-control rules. Sessions are held in a local SQLite database, so there is no external database, no Redis and no database port to secure.
Security model. Because Tinyauth is itself the thing that guards your other apps, the image ships with no usable credential at all: it contains zero configured users and the tinyauth service is held back until first boot, when it generates a fresh admin password unique to your instance, hashes it with bcrypt and writes the credential to a root-only file. Tinyauth binds to 127.0.0.1:3000 only and is fronted by nginx on port 443 with a per-instance self-signed TLS certificate; plain HTTP on port 80 permanently redirects to HTTPS. Session cookies are marked Secure. A protected demo route (/demo/) is included so you can see the auth gate working the moment the VM boots. The only ports exposed are SSH (22) and the web portal (80 and 443).
What is included:
-
Tinyauth 5.1.3 official release binary at
/usr/local/bin/tinyauth(SHA-256-verified) -
tinyauth.servicesystemd unit running the server bound to127.0.0.1:3000 -
nginxreverse proxy on port 443 (self-signed TLS; port 80 redirects to 443): the login portal, the Tinyauth API and the forward-auth endpoint, plus a demo route protected by Tinyauth -
tinyauth-firstboot.servicesystemd oneshot that mints the per-VM admin user and TLS certificate on first boot -
A local SQLite session database at
/var/lib/tinyauth/tinyauth.db -
A credential self-test at
/usr/local/sbin/tinyauth-credcheck.sh -
The AGPL-3.0 licence text at
/usr/local/share/tinyauth/LICENSE -
Ubuntu 24.04 LTS base with latest security patches applied at build time
-
24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
-
An active Azure subscription
-
A subscription to the Tinyauth on Ubuntu 24.04 listing on Azure Marketplace
-
An SSH public key for VM authentication
-
A virtual network and subnet in the target region
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). Tinyauth is extremely lightweight; scale up only if you protect many applications with very high request volumes.
Step 1: Deploy from the Azure Portal
Navigate to Marketplace in the Azure Portal, search for Tinyauth, select the cloudimg publisher entry, and click Create.
On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range and TCP 443 (and 80, which redirects to 443) from the client networks that will reach the login portal and protected apps.
Click Review + create, wait for validation, then Create. Deployment takes around two minutes.
Step 2: Deploy from the Azure CLI
RG="tinyauth-prod"
LOCATION="eastus"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" \
--name tinyauth-01 \
--image <publisher>:<offer>:<sku>:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# Allow SSH (22) and the login portal / protected apps (80 redirects to 443)
az vm open-port --resource-group "$RG" --name tinyauth-01 --port 22 --priority 1001
az vm open-port --resource-group "$RG" --name tinyauth-01 --port 80 --priority 1002
az vm open-port --resource-group "$RG" --name tinyauth-01 --port 443 --priority 1003
Replace <publisher>:<offer>:<sku> with the URN shown on the Marketplace listing's Usage Information tab.
Step 3: Connect via SSH
ssh azureuser@<vm-public-ip>
Use the private key that matches the public key you supplied at deploy time. The login user for this image is azureuser.
Step 4: Verify the Tinyauth Service
Confirm the Tinyauth server and the nginx proxy are running, and check the listening sockets. Tinyauth binds to 127.0.0.1:3000 only — it is reachable off the VM solely through nginx on port 443. The AGPL-3.0 licence ships with the image.
tinyauth version
systemctl is-active tinyauth nginx
sudo ss -ltn | grep -E ':443 |:3000 '
head -2 /usr/local/share/tinyauth/LICENSE
Expected output:
Version: v5.1.3
Commit Hash: ad700e75e065e7d966f0ba4cb16fc851cad224c6
Build Timestamp: 2026-07-30T18:00:43
active
active
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*
LISTEN 0 511 [::]:443 [::]:*
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007

Step 5: Retrieve Your Per-VM Admin Credential
On the first boot of your virtual machine, tinyauth-firstboot.service generated an admin password unique to your instance, hashed it with bcrypt into Tinyauth's configuration, and wrote the plaintext to a root-only file. Retrieve it with:
sudo cat /root/tinyauth-credentials.txt
The file reports the login URL and the admin credential:
tinyauth.url=https://<vm-public-ip>.sslip.io/
tinyauth.apphost=<vm-public-ip>.sslip.io
tinyauth.admin.user=admin
tinyauth.admin.pass=<generated-per-vm>
The URL uses sslip.io magic DNS: <vm-public-ip>.sslip.io resolves to your VM's public IP and matches the TLS certificate, so the login portal has a real hostname out of the box.
A shipped self-test confirms the whole auth-gate model without printing any secret — it proves the login portal renders over TLS, the per-VM admin credential authenticates, a wrong password is refused, the forward-auth endpoint returns 401 for an anonymous request and 200 for an authenticated one, and the protected demo route is gated:
sudo bash /usr/local/sbin/tinyauth-credcheck.sh
Expected output:
OK

Step 6: See the Auth Gate Working
Tinyauth's job is to allow authenticated requests through and bounce anonymous ones to the login portal. The image includes a demo route at /demo/ protected by Tinyauth via nginx auth_request. You can see both halves with curl — logging in to get a session cookie, then hitting the forward-auth endpoint and the protected route with and without it:
HOST=$(sudo grep '^tinyauth.apphost=' /root/tinyauth-credentials.txt | cut -d= -f2-)
AP=$(sudo grep '^tinyauth.admin.pass=' /root/tinyauth-credentials.txt | cut -d= -f2-)
C="curl -s -k --resolve ${HOST}:443:127.0.0.1"
$C -c /tmp/cj -H 'Content-Type: application/json' \
--data "{\"username\":\"admin\",\"password\":\"$AP\"}" \
-o /dev/null -w 'login -> HTTP %{http_code}\n' https://${HOST}/api/user/login
FWD="-H X-Forwarded-Proto:https -H X-Forwarded-Host:${HOST} -H X-Forwarded-Uri:/demo/"
$C $FWD -o /dev/null -w 'forward-auth anon -> HTTP %{http_code}\n' https://${HOST}/api/auth/nginx
$C $FWD -b /tmp/cj -o /dev/null -w 'forward-auth authed-> HTTP %{http_code}\n' https://${HOST}/api/auth/nginx
$C -o /dev/null -w '/demo/ anon -> HTTP %{http_code}\n' https://${HOST}/demo/
$C -b /tmp/cj -o /dev/null -w '/demo/ authed -> HTTP %{http_code}\n' https://${HOST}/demo/
rm -f /tmp/cj
Expected output:
login -> HTTP 200
forward-auth anon -> HTTP 401
forward-auth authed-> HTTP 200
/demo/ anon -> HTTP 302
/demo/ authed -> HTTP 200
The anonymous request to /demo/ returns 302 — nginx redirects it to the Tinyauth login portal. With a valid session it returns 200 and the protected page renders.

Step 7: Sign In to the Login Portal
In a browser, go to https://<vm-public-ip>.sslip.io/. Because the certificate is self-signed, your browser will warn once — accept the exception (or install a real certificate, see Step 10). Sign in with user admin and the tinyauth.admin.pass from Step 5:

Once signed in you land on the Tinyauth account page, which shows your signed-in user and a logout control. This is the session that Tinyauth uses to authorize every app you place behind it:

Step 8: Watch the Gate Protect the Demo App
Open https://<vm-public-ip>.sslip.io/demo/. While you are signed in, the protected demo page renders — nginx asked Tinyauth to authorize the request and Tinyauth let it through because you have a valid session:

If you open the same URL in a private window (no session), Tinyauth bounces you straight to the login portal — that is the entire point of the gateway. Any app you place behind Tinyauth is protected the same way.
Step 9: Protect Your Own Apps
To protect an app you run on the VM, front it with nginx and defer the authorization decision to Tinyauth. The pattern used by the demo route is:
# In your app's nginx server block:
location / {
auth_request /tinyauth-verify;
auth_request_set $tinyauth_login $upstream_http_x_tinyauth_location;
error_page 401 = @tinyauth_login;
proxy_pass http://127.0.0.1:<your-app-port>;
}
location = /tinyauth-verify {
internal;
proxy_pass http://127.0.0.1:3000/api/auth/nginx;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
location @tinyauth_login { return 302 $tinyauth_login; }
The shipped vhost at /etc/nginx/sites-available/tinyauth is a complete working example. Tinyauth also supports fine-grained per-app access-control rules (allow/block specific users or OAuth/LDAP groups per app) — see the Tinyauth access-controls guide.
Step 10: Add More Users, OAuth, OIDC and LDAP
Tinyauth's configuration lives in /etc/tinyauth/config.yaml. To add another local user, generate a bcrypt entry and add it under auth.users:
sudo tinyauth user create --interactive
# copy the printed "username:hash" value, then add it under auth.users in the config:
sudoedit /etc/tinyauth/config.yaml
sudo systemctl restart tinyauth
For single sign-on with Google, GitHub or any generic OIDC provider, add an oauth.providers block; for directory logins add an ldap block; for two-factor add a TOTP secret to a user. Each is documented in the Tinyauth configuration reference.
Step 11: Install a Real TLS Certificate (Production)
The image ships a per-VM self-signed certificate so the portal is HTTPS out of the box. For production, point a DNS name at the VM's public IP, open port 443 in the network security group, and install a Let's Encrypt certificate into the bundled nginx:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
Then update appUrl in /etc/tinyauth/config.yaml to https://your-domain.example.com/ and restart Tinyauth so redirects and cookies use the real domain:
sudoedit /etc/tinyauth/config.yaml
sudo systemctl restart tinyauth
Step 12: Managing the Tinyauth Service
# Status and logs
systemctl status tinyauth --no-pager
sudo journalctl -u tinyauth -n 100 --no-pager
# Restart after changing /etc/tinyauth/config.yaml
sudo systemctl restart tinyauth
# nginx (the reverse proxy)
sudo nginx -t && sudo systemctl reload nginx
Architecture Summary
-
Tinyauth 5.1.3 — a single statically-linked Go binary at
/usr/local/bin/tinyauth, run as the non-roottinyauthservice account under systemd, bound to127.0.0.1:3000. -
nginx terminates TLS on port 443 (self-signed per-VM certificate; port 80 redirects to 443) and serves the login portal, the Tinyauth API, the forward-auth endpoint and the protected
/demo/route. -
Configuration in
/etc/tinyauth/config.yaml— the shipped image contains no users; first boot writes the per-VMappUrland a bcrypt admin user. -
Sessions in a local SQLite database at
/var/lib/tinyauth/tinyauth.db— no external database, no database port. -
First boot (
tinyauth-firstboot.service) mints the per-VM admin password and TLS certificate, writes the root-only credentials file, then starts the stack. -
Ports: SSH (22) and the web portal (80 redirecting to 443). Tinyauth on 3000 is bound to loopback and never exposed directly.
This image is maintained by cloudimg with 24/7 support (support@cloudimg.co.uk, 24 hour response SLA).