Nhost on Ubuntu 24.04 on Azure User Guide
Overview
Nhost is an open source backend as a service, a self-hosted alternative to Firebase and Supabase that gives developers a complete application backend out of the box: a PostgreSQL database exposed over an instant GraphQL API, user authentication, S3 compatible file storage, serverless functions and a polished management dashboard. The cloudimg image installs Nhost as a pinned, single node Docker Compose stack and runs it as a systemd service, so a complete backend is online within minutes of launch.
What is included:
- The Nhost self-host stack, every image pinned at build time so the appliance ships the exact release and never silently upgrades:
- PostgreSQL 16 (the database, internal to the stack only)
- the Hasura GraphQL Engine v2.46.0 Community Edition (the instant GraphQL API plus console, on port 8080)
- the Nhost auth service 0.40.2 (on port 4000, API served under
/v1) - the Nhost storage service 0.7.2 (on port 4001), backed by MinIO (the S3 object store, internal only)
- the Nhost serverless functions runtime on Node.js 22 (on port 4002)
- the Nhost management dashboard 2.34.0 (on port 80)
- MailHog, an internal SMTP catcher for development email (internal only)
- Docker Engine (Docker CE) and the Docker Compose plugin, installed from the official Docker package repository
- All stateful data (the PostgreSQL database, the MinIO object store and the Hasura, auth and storage state) on a dedicated 40 GiB data disk mounted at
/var/lib/nhost, with Docker's data root relocated there, independently resizable and re-provisioned on every VM - Per VM secrets minted on first boot, including the PostgreSQL password, the Hasura admin secret, the Hasura JWT signing key and the MinIO access and secret keys
- Three systemd units:
docker.service,nhost.service(a oneshot wrapper around Docker Compose) andnhost-firstboot.service - 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet plus subnet. Standard_B4ms (4 vCPU / 16 GB RAM) is the recommended size, comfortable for the eight container Nhost stack. NSG inbound rules: allow 22/tcp from your management CIDR for SSH, and from the CIDR that needs the backend allow 80/tcp (dashboard), 8080/tcp (Hasura GraphQL and console), 4000/tcp (auth), 4001/tcp (storage) and 4002/tcp (functions). PostgreSQL, MinIO and MailHog are never published to the network and need no rules.
Step 1: Connect over SSH
Replace <vm-ip> with the public IP of your VM. The default login user is azureuser.
ssh azureuser@<vm-ip>
Step 2: Confirm the services are active
sudo systemctl is-active docker.service nhost.service
docker --version && docker compose version
You should see active printed twice, the Docker Engine version, and the Docker Compose v2 plugin version.

Step 3: Confirm the Nhost containers are running
cd /opt/nhost && sudo docker compose ps
The core containers, nhost-postgres-1, nhost-graphql-1, nhost-auth-1, nhost-storage-1, nhost-minio-1, nhost-dashboard-1 and nhost-mailhog-1, run in the Up (and, where a health check is defined, healthy) state. The nhost-functions-1 container restarts until you deploy a functions project, which is expected on a fresh appliance (see Step 10).

Step 4: Confirm the data disk is mounted
All stateful Nhost data lives on a dedicated data disk mounted at /var/lib/nhost, with Docker's data root relocated there.
findmnt /var/lib/nhost
df -h /var/lib/nhost
docker info --format '{{.DockerRootDir}}'
findmnt shows the device backing /var/lib/nhost, df confirms the 40 GiB volume, and Docker's data root reports as /var/lib/nhost/docker.

Step 5: Check the endpoints and read the per VM credentials
The Hasura GraphQL Engine, the auth service and the storage service each expose a health or version endpoint. A unique set of secrets is generated for your instance on its first boot and written to a root only file.
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/healthz
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:4000/healthz
curl -s http://localhost:4001/v1/version; echo
sudo cat /root/nhost-credentials.txt
The three checks return 200, and the credentials file lists the dashboard URL, the Hasura console URL, the Hasura admin secret and the backend endpoint URLs for the GraphQL, auth, storage and functions services. Store the admin secret somewhere safe.

Step 6: Open the Nhost dashboard
Browse to http://<vm-ip>/ to open the Nhost management dashboard. It presents the local project with sections for the database, the GraphQL API, Hasura, auth, storage, serverless functions and settings.

Step 7: Manage your data in the database
Open the Database section of the dashboard to create tables, add columns and browse rows in the managed PostgreSQL database. Every table you create is instantly exposed over the GraphQL API.

Step 8: Query your data over the GraphQL API
Open the GraphQL section of the dashboard to explore the schema and run queries and mutations against your data with the built in API explorer, which is already authenticated with your project's admin secret.

You can also query the API directly. The admin secret from Step 5 authorises full access:
curl -s -X POST http://localhost:8080/v1/graphql \
-H "x-hasura-admin-secret: <HASURA_ADMIN_SECRET>" \
-H "Content-Type: application/json" \
-d '{"query":"query { __typename }"}'; echo
A {"data":{"__typename":"query_root"}} response confirms the GraphQL API is up and the admin secret authenticates.
Step 9: Administer the engine in the Hasura console
The Hasura GraphQL Engine ships its own console on port 8080. Browse to http://<vm-ip>:8080/console, enter the Hasura admin secret from Step 5, and you can manage the data model, permissions, relationships, event triggers and remote schemas.

Step 10: Authentication, storage and serverless functions
The auth service signs users up and issues JWTs. This creates a user and returns an access token:
curl -s -X POST http://localhost:4000/v1/signup/email-password \
-H "Content-Type: application/json" \
-d '{"email":"demo@example.com","password":"Sup3r-Secret-123"}'; echo
The storage service serves files over an S3 compatible API on port 4001; check that it is serving:
curl -s http://localhost:4001/v1/version; echo
The serverless functions runtime is published on port 4002 and runs the JavaScript and TypeScript functions you deploy. On a fresh appliance the functions container has no project to serve and restarts until you deploy one; add a functions/ directory with your handlers (and a lock file for npm, yarn or pnpm) under /opt/nhost and recreate the stack to activate it.
Step 11: Manage the stack
The whole stack is managed by the nhost.service systemd unit, which wraps Docker Compose under /opt/nhost.
sudo systemctl status nhost.service --no-pager | head -n 6
To inspect logs for a specific service:
cd /opt/nhost && sudo docker compose logs --tail 20 graphql
Enabling HTTPS on your own domain
The image serves plain HTTP out of the box. To serve the dashboard and the backend over HTTPS on your own domain, point a DNS A record at the VM public IP, place a reverse proxy such as Caddy or Nginx with a certbot certificate for your-domain in front of the published ports, and update the NEXT_PUBLIC_NHOST_*_URL values and the CORS settings in /opt/nhost/.env to your HTTPS URLs, then restart the service. Add 443/tcp to your NSG inbound rules.
First-boot security model
On the first boot of each VM, nhost-firstboot.service runs once and:
- Mints every per VM secret in the stack: the PostgreSQL password, the Hasura admin secret, the Hasura JWT signing key (an HS256 key, regenerated per VM so tokens cannot be forged across deployments), and the MinIO storage access and secret keys
- Resolves the VM IP via Azure IMDS (falling back to the first non loopback address) and points the browser facing URLs and CORS at it
- Recreates the Compose stack on a fresh, empty database with the rotated secrets
- Refuses to start the stack if any secret still equals a published example literal, a fail closed guard, and writes the credentials to
/root/nhost-credentials.txt(root only, mode 0600)
No shared or default secret and no preexisting data ship in the image.
Support
24/7 technical support is included. Contact cloudimg at support@cloudimg.co.uk for help with deployment, the database and GraphQL API, authentication, storage, serverless functions, the dashboard, TLS and backups.
Nhost is a trademark of Nhost. Hasura is a trademark of Hasura Inc. PostgreSQL is a trademark of the PostgreSQL Community Association. MinIO is a trademark of MinIO, Inc. All other product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.