Frappe Builder 1.36.5
Overview
Frappe Builder is an open-source visual website builder built on the Frappe framework. You lay pages out on a drag-and-drop canvas, set layout, spacing, typography and colors directly, and reuse blocks as components across pages. You can also bind content to data from the Frappe database, preview pages at each device breakpoint and publish them to public routes on the same host. Frappe Builder and the Frappe framework are both MIT licensed.
This guide covers launching and using Frappe Builder 1.36.5 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud BookStack, CMS, Blogs and Wikis listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
What you get:
- Frappe Builder 1.36.5 (pinned
v1.36.5release tag) on the Frappe frameworkv16.54.0(pinned v16.54.0 release tag) - Python 3.14 (from the deadsnakes APT repository, which keeps it on the normal security-update path) and Node.js 24 LTS (the realtime server), both upstream requirements of Frappe v16, with the Builder editor pre-compiled
- HTTPS from first boot: nginx serves the site on port 443 with a certificate created on your instance's first boot, and port 80 redirects to HTTPS. The application servers listen on localhost only
- MariaDB and Redis on localhost only. The site database user authenticates with a per-instance password, and MariaDB's
rootstays on unix-socket authentication (no root password exists) - One Frappe site with the setup completed and Builder installed, with no page published
frappe-builder-firstboot.service, which runs before the application may start and creates your instance's Administrator password, database passwords and Frappe encryption keyfrappe-builder-set-hostname, which moves the site to your own DNS name- wkhtmltopdf for PDF print formats, and a functional self-test (
frappe-builder-selftest.sh) - Dependency security fixes applied on top of the upstream release (see Security notes)
Default user: ubuntu; Architecture: x86_64; Root volume: 30 GiB gp3 (sized for the Frappe bench, the MariaDB data directory and uploaded images, with room to grow).
Security posture
- No default login. A stock Frappe site signs in as
Administratorwith a password set at install time. On this image that password is discarded. The Administrator password, the site database password and Frappe'sencryption_key(which encrypts stored secrets such as an AI provider key) are all generated on each instance's first boot, before the application starts. None of them is in the image, and no two instances share one. MariaDB'sroothas no password at all (unix-socket authentication). - No build-time database. The MariaDB data directory in the image was destroyed and re-initialized from a credential-free dump of the site at build time. It holds no account, password hash or session from the build.
- Nothing is public until you publish it. Published Builder pages are public, because that is what a website builder is for. On a new instance no page is published, so every route except the sign-in page answers
404or asks for a sign-in. The editor at/builder, the Frappe desk at/appand every Builder content API refuse anonymous visitors. - Closed by default. Website sign-up is off, so nobody can create an account. You add users yourself, and no user other than Administrator exists. Web page view and click tracking are off (Builder's anonymous click-logging endpoint writes nothing). No AI provider key is set, so Builder's optional AI features make no request until you configure them.
- No anonymous OAuth client registration. Frappe v16 turns on OAuth dynamic client registration by default. This image turns it off in OAuth Settings, so nobody can register an OAuth client without signing in. Turn it back on yourself only if an OAuth or MCP client you connect needs it.
- No browser downloaded at runtime. After each publish, Builder renders a thumbnail of the page with a headless Chromium, which Frappe would otherwise download from the internet into the bench the first time. This image ships no browser and fetches none, so page thumbnails show Builder's stand-in image. Publishing and serving pages are unaffected. See Maintenance to turn thumbnails on with a browser you install.
- Fails closed. The application servers (supervisor) and nginx start only after first boot has finished and written the per-instance secrets. Supervisor also refuses to start without a database password, an encryption key and an HTTPS site address.
- Unprivileged application. gunicorn, the background workers, the scheduler and the realtime (Socket.IO) server all run as the unprivileged
frappeuser, never as root. Root-run helpers never write into thefrappeuser's files. The site configuration is edited asfrappe, refusing a symbolic link. - The application cannot reconfigure its process supervisor. supervisord runs as root, so it reads only a root-owned copy of the bench's program list (every program runs as
frappe). Its control socket is root-only (mode 0700), so thefrappeuser cannot add a program or runsupervisorctl. - Only nginx faces the network. The listening set is exactly 22, 80 and 443. gunicorn, MariaDB and Redis are bound to localhost, and the realtime server listens on a UNIX socket that only nginx can reach.
- The site address comes from configuration, never from the request. First boot reads the address from
/etc/sac/frappe-builder-hostnameor the EC2 instance metadata service (IMDSv2). It freezes the address as the site'shost_name, the nginxserver_nameand the certificate name. nginx answers only that name, and any otherHostis dropped. - Cross-site requests refused. A state-changing request or WebSocket upgrade from another site's
Originis refused. A signed-in browser cannot be made to call a Frappe API method or document method from another site (cross-siteGETincluded). - Uploads are served safely. Every response under
/files/and/private/files/carriesX-Content-Type-Options: nosniffand a sandboxing Content Security Policy. Anything other than images, PDFs, fonts, audio and video is a download, never rendered inline. - Login throttling. Every sign-in, password-reset and sign-up route shares one per-address budget in nginx, and Frappe locks an address out after repeated failures.
- No instance metadata access for the application. Builder can be asked to fetch URLs (importing remote images and fonts into a page), so the
mariadb,redis-server,nginxandsupervisorservices are blocked from reaching the instance metadata endpoints. Only the first-boot unit reads the instance's public name, through a separate transient unit. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0. - Key-only SSH. Password and keyboard-interactive logins, root login and X11 forwarding are turned off in
/etc/ssh/sshd_config.d/00-sac.conf. You sign in asubuntuwith your EC2 key pair. - Per-instance identity. SSH host keys, the machine ID, the site certificate and every password and key above are generated fresh on every instance.
Prerequisites
- An AWS account where you can subscribe to AWS Marketplace products and launch EC2 instances.
- An EC2 key pair in the target Region.
- A VPC and subnet, with a public IP address on the instance if your editors and visitors reach it over the internet.
- A security group that allows
22/tcpfrom your management network, and80/tcpand443/tcpfrom your visitors' networks. No other port is needed. Do not open 3306 or 6379.
Recommended instance type: m5.large (2 vCPU, 8 GiB RAM) suits evaluation and a small team of editors. Use m5.xlarge (4 vCPU, 16 GiB RAM) or larger for a larger site or heavier traffic.
Launch from AWS Marketplace
- Open the listing by SecureAnyCloud, choose View purchase options, then subscribe.
- Choose Continue to Configuration. Under Software version, choose
Frappe Builder 1.36.5 on Ubuntu 24.04. Pick your Region, then choose Continue to Launch. - Under Choose Action, select Launch through EC2.
- Set the instance type to m5.large (or larger) and choose your key pair. Use the seller's security group settings (ports 22, 80 and 443), and narrow port 22 to your management network. Keep the 30 GiB root volume.
- Launch the instance. First boot completes within two to three minutes of the instance reaching the running state.
Launch from the AWS CLI
Run these commands on your workstation. Replace <ami-id> with the AMI ID shown for the Frappe Builder 1.36.5 on Ubuntu 24.04 software version on the listing's launch page. Use your own VPC, subnet, key pair and address ranges.
aws ec2 create-security-group --group-name frappe-builder-sg \
--description "Frappe Builder" --vpc-id <vpc-id>
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 22 --cidr <your-ip>/32
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 80 --cidr <visitors-cidr>
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 443 --cidr <visitors-cidr>
aws ec2 run-instances --image-id <ami-id> --instance-type m5.large \
--key-name <key-name> --subnet-id <subnet-id> \
--security-group-ids <security-group-id> --associate-public-ip-address \
--metadata-options HttpTokens=required \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=frappe-builder}]'
aws ec2 describe-instances --instance-ids <instance-id> \
--query "Reservations[].Instances[].[PublicDnsName,PublicIpAddress]" --output textConnect over SSH
ssh -i <key.pem> ubuntu@<public-dns>The login user is ubuntu.
Retrieve your credentials
First boot writes your instance's credentials to a root-only file. Read it with:
sudo cat /root/frappe-builder-credentials.txtThe file looks like this. Your values differ:
builder.url=https://ec2-203-0-113-10.compute-1.amazonaws.com/builder builder.desk.url=https://ec2-203-0-113-10.compute-1.amazonaws.com/app builder.admin.login=Administrator builder.admin.pass=<generated-per-instance> mariadb.benchadmin.login=benchadmin mariadb.benchadmin.pass=<generated-per-instance>The site address is taken from /etc/sac/frappe-builder-hostname if that file exists. Otherwise it comes from IMDSv2: the instance's public DNS name, else its public IPv4 address, else its private IPv4 address.
benchadmin is a per-instance MariaDB account on localhost for bench maintenance commands.
Verify the services
systemctl is-active mariadb redis-server supervisor nginx ls /var/lib/sac/data/frappe-builder-firstboot.doneAll four services print active, and the first-boot marker exists. First boot runs once, so after a reboot its unit shows as skipped, which is expected.
Only 22, 80 and 443 listen on a public address. gunicorn (8000), MariaDB (3306), Redis (6379) and the loopback health listener (8088) are on localhost:
sudo ss -Hltn | awk '{print $4}' | sort -u0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 127.0.0.1:3306 127.0.0.1:6379 127.0.0.1:8000 127.0.0.1:8088 127.0.0.53%lo:53 127.0.0.54:53 [::1]:6379 [::]:22 [::]:443 [::]:80The two :53 lines are Ubuntu's local DNS stub resolver.
The application processes all run as the unprivileged frappe user, and the realtime server listens on a UNIX socket that only nginx's group can reach:
ps -eo user=,args= | awk '/[g]unicorn|[s]ocketio\.js|[b]ench_helper/{print $1}' | sort | uniq -c sudo stat -c '%A %U %G %n' /run/frappe-socketio/socketio.sock6 frappe srwxrwx--- frappe www-data /run/frappe-socketio/socketio.sockThe process supervisor is out of the application's reach. Its program list is a root-owned file, its control socket is root-only, and supervisorctl as the frappe user is refused:
sudo stat -c '%U:%G %a %F %n' /etc/supervisor/conf.d/frappe-bench.conf "$(readlink -f /var/run/supervisor.sock)"
sudo -u frappe supervisorctl status >/dev/null 2>&1 && echo "frappe can control supervisor" || echo "supervisorctl refused for frappe"root:root 644 regular file /etc/supervisor/conf.d/frappe-bench.conf root:root 700 socket /run/supervisor.sock supervisorctl refused for frappeThe health endpoint answers pong, the HTTPS front end on the configured name answers 200, port 80 redirects to HTTPS, and the realtime (Socket.IO) handshake through nginx answers 200:
SITE=$(sudo cat /var/lib/sac/data/frappe-builder-current-hostname)
curl -s http://127.0.0.1:8088/api/method/ping; echo
curl -sk -o /dev/null -w "%{http_code}\n" --resolve "${SITE}:443:127.0.0.1" "https://${SITE}/login"
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" --resolve "${SITE}:80:127.0.0.1" "http://${SITE}/"
curl -sk -o /dev/null -w "%{http_code}\n" --resolve "${SITE}:443:127.0.0.1" "https://${SITE}/socket.io/?EIO=4&transport=polling"{"message":"pong"} 200 301 https://ec2-203-0-113-10.compute-1.amazonaws.com/ 200A request for any other host name is dropped (nginx closes the connection, so curl prints 000):
curl -sk -o /dev/null -w "%{http_code}\n" --resolve "evil.example:443:127.0.0.1" "https://evil.example/login" || trueNothing is published on a new instance. The site's home page is the sign-in page, and the editor's content API refuses an anonymous visitor:
SITE=$(sudo cat /var/lib/sac/data/frappe-builder-current-hostname)
curl -sk -o /dev/null -w "home: %{http_code}\n" --resolve "${SITE}:443:127.0.0.1" "https://${SITE}/"
curl -sk -o /dev/null -w "page list: %{http_code}\n" --resolve "${SITE}:443:127.0.0.1" "https://${SITE}/api/resource/Builder%20Page"home: 200 page list: 403The application cannot reach the instance metadata service. This check runs the same request from inside supervisor.service (gunicorn, the workers and the realtime server) as the frappe user, and from your shell. The first prints 000 (blocked) and the second prints 200:
CG=$(systemctl show -p ControlGroup --value supervisor.service)
sudo bash -c "echo \$\$ > /sys/fs/cgroup${CG}/cgroup.procs; exec setpriv --reuid=frappe --regid=frappe --init-groups curl -s -m 5 -o /dev/null -w '%{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token" curl -s -m 5 -o /dev/null -w '%{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/tokenCheck the certificate fingerprint
The site certificate is self-signed and unique to your instance, so your browser warns you the first time you open the site. Before you continue past the warning, compare the certificate's SHA-256 fingerprint in the browser with the one on the instance. To see the fingerprint in the browser, click the padlock or warning icon, then view the certificate. To print the fingerprint on the instance, run:
sudo openssl x509 -in /etc/ssl/frappe-builder/frappe-builder.crt -noout -fingerprint -sha256Continue only if the two fingerprints match. Do not add this certificate to your computer's trusted certificates. For everyday use, install a certificate from a public certificate authority (Step 10).
Sign in to the editor
- Open the
builder.urladdress from Step 4 in a browser. Plainhttp://redirects to HTTPS. - Sign in as Administrator with the
builder.admin.passvalue. Common default passwords (admin,password123,changeme,frappe,builderand the like) are rejected. Change the password after your first sign-in, from the Frappe desk at/app.
Open /builder to reach Builder's All Pages dashboard. Filter pages by title or route, sort them, switch between grid, list and tree views, and group pages into folders. Settings holds the site-wide Builder settings.
A new instance has no pages yet (the screenshot shows one demo page). Use New to start a page from a blank canvas or from a template.
To let other people edit pages, add them as users in the Frappe desk (/app/user) with the Website Manager role. Sign-up is off, so nobody creates their own account.
Design a page
Opening a page puts you in the visual editor. The canvas in the middle shows the page as it will look. The left rail carries the block tree, the component library, assets and page scripts. The right rail shows the properties of the selected block: layout (stack or grid, direction, gap, alignment), typography, spacing, colors and borders. Switch breakpoints at the top to check the page on tablet and mobile widths.
Save blocks you reuse as components and drop them into any other page, so a header, footer or call to action is maintained in one place. Images you add are uploaded to the site's public files. PNG and JPEG uploads are converted to WebP when Builder Settings > Auto convert images to WebP is on.
Publish the page
Give the page a route in its page settings (the gear icon in the top bar), then choose Publish in the top right. The page title bar shows Draft until you publish. Builder serves the page at that route on this same host, so https://<your-site>/<your-route> is live for anonymous visitors at once.
Unpublish takes the page off the site again. A page you have not published is never served.
Only the pages you publish, and the images on them, are public. The editor, the desk and Builder's APIs still require a signed-in user with the Website Manager role.
Use your own hostname and certificate
Frappe builds absolute links (sitemaps, canonical URLs, emails) from the site's host_name, which this image sets from configuration. To serve the site on your own DNS name, point a DNS record (for example in Amazon Route 53) at the instance's public IP (an Elastic IP keeps it stable), then run this command on the instance:
sudo frappe-builder-set-hostname www.example.comThe helper writes the name to /etc/sac/frappe-builder-hostname and issues a new self-signed certificate for it. It rewrites the site host_name, the nginx server_name and the cross-site guard, updates the credentials file, and restarts the application and nginx.
The site certificate is /etc/ssl/frappe-builder/frappe-builder.crt and its key is /etc/ssl/frappe-builder/frappe-builder.key (mode 600, root). To use Let's Encrypt, first set your DNS name and open port 80 to the internet, then run:
sudo apt-get install -y certbot
sudo certbot certonly --standalone -d www.example.com \
--pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx" \
--deploy-hook "install -m 644 \$RENEWED_LINEAGE/fullchain.pem /etc/ssl/frappe-builder/frappe-builder.crt && install -m 600 \$RENEWED_LINEAGE/privkey.pem /etc/ssl/frappe-builder/frappe-builder.key"Maintenance
- Self-test. The self-test creates and publishes a page, renders it for an anonymous visitor and deletes it, which proves that Builder works end to end:
sudo /usr/local/sbin/frappe-builder-selftest.sh- Backups. Take a backup with files from the bench directory. It is written under the site's
private/backups. Copy the backup off the instance (for example to Amazon S3) on a schedule.
cd /opt/frappe/frappe-bench
sudo -u frappe bench --site builder.localhost backup --with-files- Page thumbnails. To have Builder render page thumbnails, install a Chromium or Google Chrome build that you keep updated yourself, then point Frappe at it:
sudo -u frappe bash -c "cd /opt/frappe/frappe-bench && bench set-config -g chromium_path /usr/bin/google-chrome"Then restart withsudo supervisorctl restart all. The browser then runs as thefrappeuser for every publish.
- Templates. Builder's template gallery is fetched from Frappe's template hub (
preview.frappe.cloud) when a signed-in editor opens it. Settemplate_hub_urlin the site configuration to use your own hub. - Security updates. Unattended-upgrades is enabled and applies OS security updates. Python 3.14 is apt-managed via deadsnakes and Node.js via NodeSource, and both stay on that path.
- Application updates. The
frappeandbuilderapps are pinned for a reproducible image and ship without git history. To move to a newer release, take a backup and re-fetch the app withbench get-app --overwriteat the release you want. Then runbench --site builder.localhost migrateandbench build.
Security notes
The upstream releases pin some Python and JavaScript dependencies at versions with published advisories. This image ships fixed versions, and the build fails if an audit finds anything else:
- Python (bench environment): pypdf 6.21.4, Pillow 13.0.0 and orjson 3.11.9 (hash-pinned; the image's own validation exercises them as an ordinary Website Manager: image uploads in six formats through the editor, thumbnails, a published page with an image and a page printed to PDF), GitPython 3.2.1 and filelock 4.2.4.
- Editor front end: prosemirror-view 1.42.8, the tiptap editor 3.32.0, Vue 3.5.44, DOMPurify 3.4.17 and qs 6.17.1, compiled into the shipped editor.
- Realtime server (Node.js): socket.io 4.8.4, engine.io 6.6.11, socket.io-parser 4.2.7 and ws 8.21.3, plus fixed bundled libraries.
The following residual advisories are recorded and are not reachable as described. The audit records are in /usr/share/doc/sac/frappe-builder/:
- litellm 1.95.1 (GHSA-7hp6-4w63-5g45, high): the advisory is in the LiteLLM proxy server. Builder uses litellm only as a client library for its optional AI features and pins this exact version. The proxy server is never installed or started on this image (its web framework is absent, checked at build time).
- pdfkit 1.0.0 (PYSEC-2026-2860, path traversal in
from_string, local): no fixed release exists. It is reachable only through print-format HTML, which only System Managers can author. - showdown 2.1.0 and quill 2.0.3 / 1.3.7 (moderate and low): client-side editor components reachable only by signed-in desk users. The build-time tools postcss-selector-parser and esbuild 0.14 are also recorded (never on the request path).
Troubleshooting
- The site does not answer. Check first boot with
systemctl status frappe-builder-firstboot.serviceandsudo journalctl -u frappe-builder-firstboot. Supervisor and nginx stay stopped until first boot finishes. - Certificate warning. This is expected until you install a publicly trusted certificate. Compare the fingerprint (Step 6).
- A page shows a stand-in thumbnail on the dashboard. This is expected, because no browser ships for thumbnails (see Maintenance). The published page itself is unaffected.
- 403 on a form or action. The cross-site guard refuses requests from other sites. Use the site's own address, not a raw IP that does not match the configured name.
- A user cannot open the editor. Give the user the Website Manager role in the desk.
What this image is and is not
This is Frappe Builder 1.36.5 on the Frappe framework v16.54.0, hardened and automated by SecureAnyCloud for AWS Marketplace. It adds HTTPS on first boot, per-instance secrets, a credential-free database, closed-by-default sign-up, nothing published by default, unprivileged application processes, localhost-bound data services and dependency security fixes.
SecureAnyCloud is not affiliated with or endorsed by Frappe Technologies. Frappe and Frappe Builder are trademarks of their respective owners, and the software is used under the MIT license.
Deploy on AWS
Launch Frappe Builder 1.36.5 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