Application Infrastructure Azure

Lowdefy on Ubuntu 24.04 on Azure User Guide

| Product: Lowdefy on Ubuntu 24.04 LTS on Azure

Overview

Lowdefy is an open source, config first low code web stack for building internal tools, admin panels, CRUD interfaces, dashboards and business applications. You describe an application in a single declarative lowdefy.yaml file, its pages, a rich library of UI blocks, data connections and requests, and the logic that ties them together, and Lowdefy compiles that configuration into a production web application built on Next.js. Because the whole application is reviewable, version controllable text rather than hand written frontend code, teams ship internal software quickly and consistently.

The cloudimg image builds the official Lowdefy starter app with the Lowdefy CLI into a production server under /opt/lowdefy/.lowdefy/server, runs it as the dedicated lowdefy system user bound to loopback 127.0.0.1:3000, and fronts it with nginx on port 80, ready for your TLS certificate.

Secure by default: Lowdefy has no built in login, so this image protects the application with nginx HTTP Basic authentication and there is no known bootstrap credential. On the first boot of every instance, lowdefy-firstboot.service generates a unique Basic auth password for the user admin, generates a per VM application signing secret, and writes both the URL and the password to /root/lowdefy-credentials.txt (readable only by root). The application binds to loopback only, so no world open window is ever exposed, and a static /healthz endpoint (no auth, no app content) is available for liveness probes.

What is included:

  • The official Lowdefy starter app, built with the Lowdefy CLI 5.5 into /opt/lowdefy/.lowdefy/server
  • The app config at /opt/lowdefy/lowdefy.yaml, ready for you to edit and rebuild
  • lowdefy.service running as lowdefy, bound to 127.0.0.1:3000
  • nginx reverse proxy on port 80 with HTTP Basic auth, ready for TLS
  • lowdefy-firstboot.service for the per VM Basic auth password and signing secret
  • Node.js 22 LTS runtime
  • Ubuntu 24.04 LTS base, fully patched
  • 24/7 cloudimg support, 24h response SLA

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet with a subnet. Recommended VM size: Standard_B2s (Lowdefy is lightweight; 4 GB RAM is comfortable).

Step 1: Deploy from the Azure Portal

Search the Marketplace for Lowdefy on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network and TCP 80 / 443 (the web app) from the networks that need it. Front port 80 with TLS in production (see the HTTPS section below).

Step 2: Deploy from the Azure CLI

RG="lowdefy-prod"; LOCATION="eastus"; VM_NAME="lowdefy-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/lowdefy-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name ld-vnet --address-prefix 10.90.0.0/16 --subnet-name ld-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name ld-nsg
az network nsg rule create -g "$RG" --nsg-name ld-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name ld-nsg --name allow-web --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 443 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name ld-vnet --subnet ld-subnet --nsg ld-nsg --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

Step 4: Verify the services are running

The Lowdefy server listens on loopback 127.0.0.1:3000; nginx fronts the web app on port 80.

sudo systemctl is-active lowdefy nginx
ss -tln | grep -E ':80 |127.0.0.1:3000'
curl -s -o /dev/null -w 'GET /healthz -> HTTP %{http_code}\n' http://127.0.0.1/healthz

Both services report active, the Lowdefy server is bound to 127.0.0.1:3000, nginx is bound to :80, and the liveness endpoint returns HTTP 200 through nginx.

lowdefy.service and nginx.service active, the Lowdefy server bound to 127.0.0.1:3000, nginx on port 80, and the health endpoint returning HTTP 200

Step 5: Read your per VM credentials

There is no default login. On first boot a unique Basic auth password and application signing secret were generated for this instance and written to a root only file. Lowdefy has no built in login, so the application is protected by nginx HTTP Basic auth.

sudo stat -c '%n perms=%a owner=%U:%G' /root/lowdefy-credentials.txt
sudo grep -E '^LOWDEFY_URL|^LOWDEFY_USERNAME' /root/lowdefy-credentials.txt
curl -s -o /dev/null -w 'unauthenticated GET / -> HTTP %{http_code}\n' http://127.0.0.1/

The credentials file is 600 root:root, it records the resolved URL and the username admin, and an unauthenticated request to / is refused with HTTP 401. Read your password with sudo grep '^LOWDEFY_PASSWORD' /root/lowdefy-credentials.txt.

The root only per VM credentials file, its URL and username, and an unauthenticated request refused with HTTP 401

Step 6: Confirm the credential authenticates

Use the per VM password to confirm the Basic auth gate accepts your credential end to end.

PW=$(sudo grep '^LOWDEFY_PASSWORD=' /root/lowdefy-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authenticated GET / -> HTTP %{http_code}\n' -u "admin:$PW" http://127.0.0.1/

The authenticated request is accepted and returns an HTTP redirect to the app home page (307), proving the credential works while unauthenticated access is refused.

Step 7: Open the web UI

Open http://<vm-ip>/ in your browser. Your browser prompts for the Basic auth credential; enter the username admin and the password from /root/lowdefy-credentials.txt. The Lowdefy starter app then loads its welcome page.

The Lowdefy starter app welcome page after signing in with the per VM Basic auth credential

Use the header menu to move between the pages. The Getting started page explains how to build your own app.

The Lowdefy starter app Getting started page, reached from the header menu

The Capabilities page summarises what you can build with Lowdefy from configuration alone.

The Lowdefy starter app Capabilities page describing what you can build from configuration

Step 8: Build your own low code app

The app is defined entirely by /opt/lowdefy/lowdefy.yaml. Confirm it is present and editable, and that the current build is in place:

sudo test -f /opt/lowdefy/lowdefy.yaml && echo "config present at /opt/lowdefy/lowdefy.yaml"
sudo test -d /opt/lowdefy/.lowdefy/server/.next && echo "current compiled build present"

To build your own app, edit /opt/lowdefy/lowdefy.yaml to add pages, blocks, connections and requests, then recompile and restart the service:

  • Rebuild: sudo -u lowdefy bash -c 'cd /opt/lowdefy && lowdefy build'
  • Restart: sudo systemctl restart lowdefy

lowdefy build recompiles the configuration into the Next.js server under /opt/lowdefy/.lowdefy/server, and restarting lowdefy.service serves your updated app. See the Lowdefy documentation at docs.lowdefy.com for the block, operator and connection reference.

Step 9: The runtime stack

node -v
nginx -v
test -f /opt/lowdefy/lowdefy.yaml && echo "config present"
test -d /opt/lowdefy/.lowdefy/server/.next && echo "next build present"

Node.js 22 LTS runs the server, nginx 1.24 fronts it, and both the app configuration and the compiled Next.js server are present on disk.

Node.js 22 LTS, nginx 1.24, the Lowdefy configuration and the compiled Next.js server present on disk

Step 10: First boot and patch baseline

test -f /var/lib/cloudimg/lowdefy-firstboot.done && echo "firstboot: completed"
sudo stat -c '%n perms=%a owner=%U:%G' /etc/nginx/.lowdefy_htpasswd
apt-mark showhold

The first boot marker confirms the per VM password and signing secret were provisioned, the nginx Basic auth password file is present and root owned, and there are no held packages, so the OS security baseline is intact.

The first boot completion marker, the root owned Basic auth password file, and an intact OS patch baseline with no held packages

Step 11: Change the Basic auth password

Rotate the per VM password at any time with htpasswd. Run sudo htpasswd /etc/nginx/.lowdefy_htpasswd admin and enter a new password when prompted. To add another user, run sudo htpasswd /etc/nginx/.lowdefy_htpasswd <username>. No nginx reload is needed; the password file is read on each request. Confirm the password file is in place and root owned:

sudo stat -c '%n perms=%a owner=%U:%G' /etc/nginx/.lowdefy_htpasswd

Step 12: Enable HTTPS

nginx already fronts Lowdefy on port 80 and is ready for TLS. Point a DNS record at your VM, then obtain a certificate with certbot:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain> --agree-tos -m <your-email> --redirect

certbot installs the certificate, rewrites the nginx server block for TLS, and sets up automatic renewal. Your app is then served over HTTPS with Basic auth in front.

Managing the service

sudo systemctl status lowdefy --no-pager | head -n 6
sudo journalctl -u lowdefy -n 15 --no-pager

Restart the server with sudo systemctl restart lowdefy and follow its logs live with sudo journalctl -u lowdefy -f. The Lowdefy server logs to the journal; nginx logs are under /var/log/nginx/. Back up the instance by snapshotting the OS disk or copying /opt/lowdefy while the service is stopped.

Support

cloudimg provides 24/7 support for this image via email and live chat: deployment, nginx TLS termination, Basic auth and optional Lowdefy authentication providers, upgrades, and building your Lowdefy configuration. Email support@cloudimg.co.uk.

This is a repackaged open source software product with additional charges for cloudimg support services. Lowdefy is a trademark of its respective owner. All 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.