Frappe Builder on Ubuntu 24.04 on Azure User Guide
Overview
Frappe Builder is an open-source visual website builder built on the Frappe framework. It gives you a drag and drop canvas for laying out pages, direct control over styling, spacing and typography, reusable components you can share across pages, a scripting and data binding layer for dynamic content, previews across device breakpoints, and one click publishing of finished pages to public routes on the same host. The cloudimg image installs Builder v1.31.0 on Frappe framework v15.116.0 as a full production deployment - MariaDB, Redis, a Python gunicorn web tier plus a node realtime process, background workers and a scheduler managed by supervisor, and nginx on port 80 - runs the entire headless install at build time so there is no command-line bootstrap on first use, and rotates the Frappe Administrator password, the site database password and the Frappe encryption key on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- Frappe Builder v1.31.0 on Frappe framework v15.116.0, served on port 80 by nginx in front of a gunicorn web worker
- MariaDB on loopback (Barracuda / utf8mb4 / large packets per Frappe), tuned for a small VM
- Redis for cache, queue and socketio; supervisor managing the Frappe web, worker, schedule and socketio processes
- The node realtime process on a unix socket rather than a network port, so nothing extra is exposed
- A unique Administrator password, site database password and Frappe encryption key generated on first boot, with no default login, and anonymous self-registration disabled
nginx.service,supervisor.service,mariadb.serviceandredis-server.serviceas systemd units, enabled and active- 24/7 cloudimg support
Frappe Builder and the Frappe framework are both MIT licensed, so there is no per seat, per page or per site fee.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is the recommended size - the interactive canvas and any re-run of the front end asset build are the memory peak. Standard_B2s (2 vCPU / 4 GiB) is the supported minimum for light use. NSG inbound: allow 22/tcp from your management network and 80/tcp for the Builder studio and for the pages you publish (front with TLS for public exposure - see Enabling HTTPS).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Frappe Builder by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name frappe-builder \
--image <marketplace-image-urn> \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name frappe-builder --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the Builder stack is running
On first boot the image rotates the Frappe encryption key, the site database password and the Administrator password, points the site at this VM's public IP, and starts the production stack. Confirm all four systemd services are active:
systemctl is-active mariadb redis-server supervisor nginx
All four report active:
active
active
active
active
Supervisor runs the Frappe web, socketio, worker and schedule processes:
supervisorctl status
frappe-bench-web:frappe-bench-frappe-web RUNNING pid 8714, uptime 0:03:25
frappe-bench-web:frappe-bench-node-socketio RUNNING pid 8715, uptime 0:03:25
frappe-bench-workers:frappe-bench-frappe-long-worker-0 RUNNING pid 8718, uptime 0:03:25
frappe-bench-workers:frappe-bench-frappe-schedule RUNNING pid 8716, uptime 0:03:25
frappe-bench-workers:frappe-bench-frappe-short-worker-0 RUNNING pid 8717, uptime 0:03:25

Confirm the Frappe web tier answers its health endpoint:
curl -s http://127.0.0.1/api/method/ping
Frappe returns {"message":"pong"}. You can see the exact installed apps and versions - Frappe framework 15.116.0 and Builder 1.31.0:
sudo -u frappe -H bash -lc 'cd /opt/frappe/frappe-bench && bench --site builder.localhost list-apps'
frappe 15.116.0 HEAD
builder 1.31.0 HEAD
Only ports 22 and 80 are reachable from the network. MariaDB, Redis and the gunicorn web worker are bound to loopback, and the node realtime process listens on a unix socket rather than a TCP port:
ss -lntuH | awk '{print $1, $5}' | sort -u
tcp 0.0.0.0:22
tcp 0.0.0.0:80
tcp 127.0.0.1:3306
tcp 127.0.0.1:6379
tcp 127.0.0.1:8000
tcp 127.0.0.53%lo:53
tcp 127.0.0.54:53
tcp [::1]:6379
tcp [::]:22
tcp [::]:80
udp 10.0.0.14%eth0:68
udp 127.0.0.1:323
udp 127.0.0.53%lo:53
udp 127.0.0.54:53
udp [::1]:323

Step 5 - Retrieve the Administrator password
The per-VM Administrator password is generated on first boot and written to a root-only credentials file, along with the studio URL and login name:
sudo cat /root/frappe-builder-credentials.txt
The file is mode 0600, owned by root:
sudo stat -c '%a %U:%G %n' /root/frappe-builder-credentials.txt
600 root:root /root/frappe-builder-credentials.txt

Because these secrets are generated on first boot, no two deployments share the same password, the published vendor defaults are rejected, anonymous visitors cannot read or write Builder content, and self-registration is refused:

You can reproduce the core of that check yourself. Sign in through the API with the password from your credentials file and fetch the studio with that session:
curl -s -c /tmp/builder-cookies -o /dev/null -w 'login: %{http_code}\n' \
http://127.0.0.1/api/method/login \
--data-urlencode 'usr=Administrator' --data-urlencode 'pwd=<BUILDER_ADMIN_PASSWORD>'
curl -s -b /tmp/builder-cookies -o /dev/null -w 'builder: %{http_code}\n' http://127.0.0.1/builder
rm -f /tmp/builder-cookies
Both requests return 200.
The boundary that protects your work is at the API layer, not at the front door. Requesting /builder without a session also returns 200 - Frappe serves the studio's static application shell to anyone - but that shell contains none of your content: the studio's router sends an unauthenticated visitor straight to /login, the Frappe desk at /app is gated behind sign-in, and every Builder content API refuses an anonymous caller. Reading the page list and attempting a write both return 403:
curl -s -o /dev/null -w 'anon page list: %{http_code}\n' \
'http://127.0.0.1/api/method/frappe.client.get_list?doctype=Builder%20Page&limit_page_length=5'
curl -s -o /dev/null -w 'anon write: %{http_code}\n' -X POST \
-H 'Content-Type: application/json' -d '{"page_title":"anon"}' \
'http://127.0.0.1/api/resource/Builder%20Page'
anon page list: 403
anon write: 403
This is deliberate. Publishing pages to public routes is what the product is for, so the image does not put a blanket gate in front of nginx - it enforces the boundary on the data instead.
Step 6 - Sign in to Frappe Builder
Browse to http://<vm-public-ip>/builder and sign in as Administrator with the password from Step 5. Change this password after your first sign-in, from the Frappe desk at /app. There is no sign-up link on this page because anonymous self-registration is disabled in this image; you add further users yourself from the desk.

Step 7 - Work with pages
After signing in you land on All Pages, which lists every page in this workspace as a card with its title and when it was last edited. Filter by title or route, change the sort order, switch between grid, list and tree views, group pages into folders from the sidebar, and use New to start a page from a blank canvas or from a template. A fresh instance starts empty - the image ships with no sample pages.

Step 8 - Design a page on the canvas
Opening a page puts you in the visual editor. The canvas in the middle renders the page as it will look; the left rail carries the block tree, the component library, the asset browser and the script editor; and the right rail shows the properties of whatever block you select - link, layout (stack or grid, direction, placement, distribution, gap, wrap), typography (family, weight, size) and style (opacity, background, borders). Select an element on the canvas and edit it directly, drag blocks to reorder them, and use the device selector above the canvas to check the layout at each breakpoint.

Blocks you expect to reuse can be saved as components and dropped into any other page, so a header, footer or call to action is maintained in one place. The script editor attaches client scripts and data bindings to a page when you need content that comes from the database rather than from the canvas.
Step 9 - Publish the page
Give the page a route, then use Publish Changes in the top right. Frappe Builder renders the page and serves it at that route on this same host, so http://<vm-public-ip>/<your-route> is live immediately to anonymous visitors - which is exactly what a website builder is for. Point a DNS record at the VM and add TLS (below) to serve it on your own domain.

Note that published pages are intentionally public - that is the point of the product. Everything else is not: the studio's router sends an unauthenticated visitor to the sign-in page, the Frappe desk requires a session, and the Builder content APIs refuse anonymous reads and writes (see Step 5). So only the pages you explicitly publish are reachable without signing in.
Enabling HTTPS
For public exposure, front the site with TLS. Point a DNS A record at the VM's public IP, then issue a certificate with Certbot and the nginx plugin (run interactively):
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
Certbot edits the Frappe nginx site in place and sets up automatic renewal. Allow 443/tcp in your NSG. Then tell Frappe its public address so generated links and redirects use it:
sudo -u frappe -H bash -lc "cd /opt/frappe/frappe-bench && bench --site builder.localhost set-config host_name 'https://your-domain.example.com'"
sudo systemctl restart supervisor
Maintenance
The OS keeps receiving security updates through unattended-upgrades. Frappe Builder and the framework can be updated with bench as the frappe user (sudo -u frappe -H bash -lc 'cd /opt/frappe/frappe-bench && bench update'); review the Builder release notes before applying a major version upgrade. Take a snapshot of the OS disk before any upgrade. The full dependency licence inventory for this image ships at /usr/share/frappe-builder/dependency-licences.txt.
Support
This image is published and supported by cloudimg. Frappe and Frappe Builder are trademarks of Frappe Technologies Pvt. Ltd.; cloudimg is not affiliated with or endorsed by the Frappe project. For assistance, contact cloudimg support.