Frappe Education 16.1.0
Overview
Frappe Education is an open source student information and school management application built on the Frappe framework. It covers admissions and student applicants, student and guardian records, the academic structure (programs, courses, academic years and terms), program enrollment, student groups, instructors, course scheduling, attendance, assessment plans and results, and fee structures and schedules. Frappe Education and the Frappe framework are licensed under the GNU General Public License version 3 (GPL 3.0).
Frappe Education requires ERPNext. Its fee and accounting features post to an ERPNext company's chart of accounts, and its admissions and customer machinery is ERPNext's. This image therefore ships the full ERPNext v16.53.0 suite on Frappe v16.54.0, with the ERPNext setup wizard already completed and a neutral starter company ("Demo Company"). No student, guardian, admission or fee records are seeded, so school records are yours to enter.
This guide covers Frappe Education 16.1.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Moodle & E-Learning Platforms AMIs listing on AWS Marketplace, and you select it as the software version when you subscribe and launch. The listing's guide hub describes the other learning platforms in the listing.
What you get:
- Frappe Education 16.1.0 (pinned
v16.1.0release tag) on Frappe v16.54.0, with the full ERPNext v16.53.0 suite (a hard dependency). All three apps are pinned to their release tags. - Python 3.14 (from the deadsnakes APT repository, which keeps it on the normal security-update path) and Node.js 24, both upstream hard requirements of Frappe v16, with the bench's front-end assets (frappe, ERPNext and Education) pre-compiled
- HTTPS from first boot: nginx serves the site on port 443 with a certificate created on your instance's first boot (an end-entity certificate for the site name, never a certificate authority), 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 ERPNext setup wizard completed, so the desk opens straight to work instead of looping through the wizard
frappe-education-firstboot.service, which runs before the application may start and creates your instance's Administrator password, site database password and Frappe encryption keyfrappe-education-set-hostname, which moves the site to your own DNS name- wkhtmltopdf for PDF print formats, and a functional self-test (
frappe-education-selftest.sh) - Dependency security fixes applied on top of the upstream release (see Security notes)
Default user: ubuntu. Architecture: x86_64. Root volume: 40 GiB gp3, sized for the Frappe bench, its Node.js modules, the ERPNext suite and the MariaDB data directory, with room to grow.
Security posture
- No default login. A stock Frappe/ERPNext 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 email passwords) 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), so there is no database root credential to steal. - 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, so it holds no account, password hash or session from the build. First boot creates the database accounts with this instance's own passwords.
- Fails closed. The application servers (supervisor) and nginx start only after first boot has finished and written the per-instance secrets. Supervisor additionally refuses to start without a database password, an encryption key and an HTTPS site address. If first boot cannot finish, the site stays down rather than open.
- Unprivileged application. gunicorn, the background workers, the scheduler and the realtime (Socket.IO) server all run as the unprivileged
frappeuser, never as root. - The application cannot reach root through supervisor. supervisor runs as root, so its program file
/etc/supervisor/conf.d/frappe-bench.confis a root-owned file generated at build time (not a link to thefrappeuser's bench configuration), every program in it runs asfrappe, and its control socket is usable by root only. Code running asfrappecan neither add a program nor start one. - 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. 3306 and 6379 are never exposed.
- The site address comes from configuration, never from the request. First boot reads it from
/etc/sac/frappe-education-hostnameor the EC2 instance metadata service (IMDSv2) and freezes it as the site'shost_name, the nginxserver_name, and the certificate name. nginx answers only that name and drops any other Host or SNI, so a request cannot rewrite the links in emails and reset messages. nginx overwritesX-Forwarded-Forwith the real client address, so a client cannot spoof its IP. - Cross-origin requests refused. A state-changing request or a WebSocket upgrade that carries another site's
Originheader is refused by nginx before it reaches the application. Frappe also runs many API methods on a plain GET and checks its CSRF token only on POST, PUT, DELETE and PATCH, so a link on another site could otherwise call them as you. When a browser reports that a GET to the Frappe API (/api/...or a?cmd=request) came from another site, or was opened from outside the browser, nginx removes your session cookie and the request runs as Guest, which Frappe refuses for every signed-in method. The desk itself, guest links such as unsubscribe, and API clients using API keys are unaffected. No route is exempt, so Frappe's Google integrations (Google Drive backups, Calendar, Contacts), which are not configured on a fresh instance, cannot complete their "Allow" step on this image: Google's redirect back is a cross-site request and runs as Guest. - No instance metadata access for the application. Frappe and ERPNext can be asked to fetch URLs (LTI, RSS, data imports, webhooks, print-format and website image fetches, background jobs), so the
mariadb,redis-server,nginxandsupervisorservices are blocked from reaching the instance metadata endpoints (169.254.169.254, its IPv4-mapped IPv6 form and fd00:ec2::254). Only the first-boot unit reads metadata, for the site address. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - 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 the site database password, Administrator password and Frappe encryption key are all 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 users reach it over the internet.
- A security group that allows
22/tcpfrom your management network, and80/tcpand443/tcpfrom your users' 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 single school or small institution. Because the image ships the full ERPNext v16 suite, use m5.xlarge (4 vCPU, 16 GiB RAM) or larger when many users work concurrently.
Launch from AWS Marketplace
- Open the Hardened Moodle & E-Learning Platforms AMIs listing by SecureAnyCloud, choose View purchase options, then subscribe.
- Choose Continue to Configuration. Under Software version, choose
Frappe Education 16.1.0 on Ubuntu 24.04, pick your Region, and choose Continue to Launch. - Under Choose Action, select Launch through EC2.
- Set the instance type to m5.large (or m5.xlarge for many concurrent users) and choose your key pair. Use the seller's security group settings (ports 22, 80 and 443), narrowing port 22 to your management network. Keep the 40 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 from your workstation. Replace <ami-id> with the AMI ID shown for the Frappe Education 16.1.0 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-education-sg \
--description "Frappe Education" --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 <users-cidr>
aws ec2 authorize-security-group-ingress --group-id <security-group-id> \
--protocol tcp --port 443 --cidr <users-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-education}]'
aws ec2 describe-instances --instance-ids <instance-id> \
--query "Reservations[].Instances[].[PublicDnsName,PublicIpAddress]" --output textConnect over SSH
Connect to the instance:
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:
sudo cat /root/frappe-education-credentials.txtThe file looks like this (your values differ):
education.url=https://ec2-203-0-113-10.compute-1.amazonaws.com/app/education education.desk.url=https://ec2-203-0-113-10.compute-1.amazonaws.com/app education.admin.login=Administrator education.admin.pass=<generated-per-instance>The site address is taken from /etc/sac/frappe-education-hostname if that file exists, otherwise from IMDSv2: the instance's public DNS name, else its public IPv4 address, else its private IPv4 address.
If you keep credentials centrally, copy them into AWS Secrets Manager.
Verify the services
Check the services and the first-boot marker:
systemctl is-active mariadb redis-server supervisor nginx ls /var/lib/sac/data/frappe-education-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 (gunicorn, the realtime server, the scheduler and the workers) all run as the unprivileged frappe user, and the realtime server listens on a UNIX socket owned by frappe and readable only by nginx's group:
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 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=$(sed -n 's/^ *server_name \([^_;][^;]*\);/\1/p' /etc/nginx/conf.d/frappe-education.conf | head -1)
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" || trueThe application cannot reach the instance metadata service. The commands below run 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/tokenSign in
- Open the
education.desk.urladdress from Step 4 in a browser. Plainhttp://redirects to HTTPS. - The certificate is self-signed and unique to your instance, so the browser warns the first time. Before you continue, compare the SHA-256 fingerprint the browser shows with the one the instance prints (do not add the certificate to a trust store), or install a trusted certificate (Step 8):
sudo openssl x509 -in /etc/ssl/frappe-education/frappe-education.crt -noout -fingerprint -sha256Then sign in as Administrator with the education.admin.pass value.
The desk opens on the app launcher (ERPNext, Education and Framework), not the setup wizard, because the ERPNext setup was completed when the image was built. Choose Education to open its workspace and reach programs, courses, students, admissions, attendance, assessment and fees.
Common default passwords (admin, password123, changeme, frappe, erpnext and the like) are rejected. Only the per-instance password works. Change it after your first sign-in under My Settings.
Use your own hostname
Frappe builds the links in emails and password-reset messages 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 on the instance:
sudo frappe-education-set-hostname school.example.orgThe helper writes the name to /etc/sac/frappe-education-hostname, issues a new self-signed certificate for it, rewrites the site host_name, the nginx server_name and the cross-origin allow-list, updates the credentials file, and restarts the application and nginx. An instance without an Elastic IP gets a new public DNS name when it is stopped and started; run the helper with the new name so links and the certificate follow it.
Install a trusted certificate
The site certificate is /etc/ssl/frappe-education/frappe-education.crt and its key /etc/ssl/frappe-education/frappe-education.key (mode 600, root). To use Let's Encrypt, first set your DNS name (Step 7) and open port 80 to the internet. Then run:
sudo apt-get install -y certbot
sudo certbot certonly --standalone -d school.example.org \
--pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx" \
--deploy-hook "install -m 644 \$RENEWED_LINEAGE/fullchain.pem /etc/ssl/frappe-education/frappe-education.crt && install -m 600 \$RENEWED_LINEAGE/privkey.pem /etc/ssl/frappe-education/frappe-education.key"Your starter company
Because Education requires ERPNext, the ERPNext setup wizard was completed at build time, which creates a company. The image ships a neutral Demo Company (GBP, United Kingdom) so fee and accounting features work immediately. Rename it under Accounting, then Company, or create your own company and set it as the default.
You own your compliance posture for any student data you enter. SecureAnyCloud makes no FERPA or GDPR claims about the image.
Maintenance
- Self-test. The self-test creates a Program and a Course, reads them back and deletes them, proving the Education module works end to end:
sudo /usr/local/sbin/frappe-education-selftest.sh- Backups. Run a backup with files from the bench directory. It is written under the site's
private/backups. Copy it off the instance (for example to Amazon S3) on a schedule.
cd /opt/frappe/frappe-bench
sudo -u frappe bench --site education.localhost backup --with-files- Security updates. Unattended-upgrades is enabled and applies OS security updates. Python 3.14 is apt-managed via deadsnakes and stays on that path.
- Application updates. The
frappe,erpnextandeducationapps are pinned for a reproducible image and ship without git history (no.gitdirectories). To move to a newer release, take a backup and re-fetch the app withbench get-app --overwriteat the release you want, thenbench --site education.localhost migrateandbench build, in a maintenance window.bench setup supervisoronly rewrites the bench's ownconfig/supervisor.conf, which supervisor does not read. The programs supervisor runs are defined in the root-owned/etc/supervisor/conf.d/frappe-bench.conf. If an update adds a process, review that file, and keepuser=frappeon every program. Re-fetching an app re-applies upstream's own dependency pins, which are older than the fixed versions this image ships (see Security notes); re-checkpip-auditandnpm auditafter an update.
Security notes
The upstream Frappe v16 release pins several Python and JavaScript dependencies at versions with published advisories. This image ships fixed versions, the same ones Frappe's own development branch has moved to, and the build fails if an audit finds anything else:
- Python (bench environment): Pillow 13.0.0 (Frappe v16 pins the 12.x line, including heap-overflow advisories reachable through image uploads), pypdf 6.21.4 (Frappe pins 6.19.0; 6.21.3 and 6.21.4 fix five October 2026 advisories in PDF parsing) and orjson 3.11.9, installed from hash-pinned wheels, plus GitPython 3.2.1 and filelock 4.2.4. The image's own validation checks the exact running versions and exercises them through Frappe as a normal (non-administrator) user: uploading PNG, JPEG, GIF, TIFF, WebP and BMP images, previewing attachments, generating thumbnails, a program image, an image on a published web page, and printing a document to PDF.
- 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, which fix denial-of-service advisories in the Socket.IO polling and WebSocket transports, plus fixed lodash, moment, nanoid, minimatch, picomatch, svgo and other bundled libraries. The unused
@vue/component-compilerpackage and its vulnerable dependency chain are removed. - OS packages: fully patched at build. Ubuntu's
python3-pip,python3-pip-whlandpython3-wheelpackages (security fixes only in Ubuntu Pro ESM) are removed because nothing on the appliance uses them.
Recorded residual advisories (moderate or low, with no fixed release inside Frappe v16's toolchain). The audit records are in /usr/share/doc/sac/frappe-education/:
- pdfkit 1.0.0 (PYSEC-2026-2860, path traversal in
from_string, local): no fixed release exists. Reachable only through print-format HTML, which only System Managers can author. - showdown 2.1.0 (Markdown preview cross-site scripting and ReDoS, moderate) and quill 2.0.3 / 1.3.7 (HTML export cross-site scripting, low, and the vendor-disputed CVE-2021-3163, moderate): client-side editor components, reachable only by signed-in desk users. Give desk access only to trusted staff.
- postcss-selector-parser (through cssnano 5) and esbuild 0.14 with esbuild-plugin-vue3 (moderate): build-time tools that run only when an administrator runs
bench buildon the image's own source, never on the request path.
sudo python3 -c "import json;d=json.load(open('/usr/share/doc/sac/frappe-education/npm-audit-frappe.json'));print(d['metadata']['vulnerabilities']);print(sorted(d['vulnerabilities']))"{'info': 0, 'low': 0, 'moderate': 6, 'high': 0, 'critical': 0, 'total': 6} ['esbuild', 'esbuild-plugin-vue3', 'frappe-quill-image-resize', 'postcss-selector-parser', 'quill', 'showdown']Troubleshooting
- The site does not answer. Check first boot:
systemctl status frappe-education-firstboot.serviceandsudo journalctl -u frappe-education-firstboot. Supervisor and nginx stay stopped until first boot finishes. - Certificate warning. This is expected until you install your own certificate, because the first-boot certificate is self-signed. Compare its fingerprint as in Step 6.
- "Not permitted" on an API link opened from another site or typed into the address bar. This is expected. Such GET requests to
/api/...run as Guest (see Security posture). Open the record from the desk instead. Links the desk itself opens, such as PDF downloads, keep your session. Because the request reaches Frappe without your session, Frappe answers it as a new Guest visit and the browser drops your sign-in, so sign in again afterwards. - 403 on a form or action. The origin guard refuses cross-origin state-changing requests. Use the site's own address, not a raw IP that does not match the configured name.
- Live updates do not appear. The realtime server runs under supervisor as
frappe-bench-web:frappe-bench-node-socketio. Check it withsudo supervisorctl statusand its log in/var/log/frappe-education/node-socketio.error.log(supervisor writes every program log under/var/log/frappe-education/).
What this image is and is not
This is Frappe Education 16.1.0 with the ERPNext v16.53.0 suite, hardened and automated by SecureAnyCloud for AWS Marketplace, with HTTPS on first boot, per-instance secrets, a credential-free database, unprivileged application processes, localhost-bound data services, dependency security fixes and the setup wizard completed. It is not affiliated with or endorsed by Frappe Technologies. Frappe, ERPNext and Education are trademarks of their respective owners, and the software is used under GPL 3.0.
Deploy on AWS
Launch Frappe Education 16.1.0 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