Enaki
Intercultural bilingual teacher-training school in the Peruvian Amazon
The full web ecosystem of a teacher-training institution: a public site for programs, courses and news, an API for contact and complaints, and a Moodle virtual campus, all on infrastructure I set up and maintain.
- Role
- Design, front end, back end, infrastructure and operations
- Year
- 2026
- Status
- Live· enaki.edu.pe

Where it started
Before
- Site and domain on a shared hosting plan at Punto.pe
- Nothing in front of the server: no CDN, no DDoS or bot filtering
- No virtual campus and no backend of its own
Now
- DNS and proxy on Cloudflare: CDN, DDoS mitigation, bot protection and HTTPS enforced with TLS 1.2+
- Static front end on Hostinger; API and Moodle on a hardened VPS with Docker
- The server only accepts traffic from Cloudflare, so its IP is never exposed
- Encrypted daily backups stored off the server
Architecture
The path of a request, from the browser down to the database.
- 01
Hostinger · enaki.edu.pe
The site the visitor loads: static files, with no application server to attack. When someone sends a form or opens the virtual campus, the request heads to the VPS.
- 02
Cloudflare
DNS, CDN and proxy in front of everything. Absorbs DDoS attacks, filters bots and enforces HTTPS before a request reaches the VPS.
- 03
Caddy on the VPS
The only way into the VPS. Serves Cloudflare's IP ranges only, with a Cloudflare Origin certificate (Full strict) and security headers.
- 04
FastAPI · Moodle
api.enaki.edu.pe handles contact and complaints; aula.enaki.edu.pe is the virtual campus. Each runs in its own container.
- 05
PostgreSQL · MariaDB
On private Docker networks: no published port and no route to the internet. Only their own application can reach them.
Security
Origin hidden behind Cloudflare
A firewall on the DOCKER-USER chain (Docker bypasses UFW) and Caddy itself both drop any connection that doesn't come from Cloudflare. Two layers, in case one fails.
Rate limiting by real IP
Limits use the visitor's real address from CF-Connecting-IP, not the proxy's: 5 messages per minute on the contact form, 3 complaints every 10 minutes.
Strict validation
Pydantic checks every field against Peruvian formats (DNI, mobile numbers), and outgoing e-mail rejects header injection.
Minimal surface
The API only accepts POST from the site's own domains, hides its docs in production and doesn't announce server versions. JSON responses can't be framed.
Locked-down server access
No root login and no passwords: Ed25519 SSH keys only, with a least-privilege admin user and a default-deny firewall.
Operations
Encrypted daily backups
Moodle's database and files plus the site's PostgreSQL, encrypted with rclone crypt and shipped to Google Drive with 30 days of retention. Restore steps are documented next to the script.
Privacy by schedule
Contact messages delete themselves after two years, exactly as the privacy policy promises under Peru's data-protection law.
Reproducible deploys
One Docker Compose file describes the whole server, with pinned image versions, health checks, rotated logs and migrations applied on start. CI builds the site and tests the API against a real PostgreSQL.
What I built
Moodle virtual campus
A custom Moodle 5 image on PHP 8.4: document root moved to /public as Moodle now requires, the PHP extensions it asks for, and its cron running every minute.
Online complaints book
The complaints book Peruvian law requires, with yearly correlative numbering protected by a row lock and the mandatory copy e-mailed to the consumer.
Public site
Programs, a 10-cycle curriculum, courses and news, with self-hosted fonts and content kept in versioned data files instead of a CMS.
Institutional e-mail
Mail on the enaki.edu.pe domain with SPF, DKIM and DMARC, so messages from the site land in the inbox and not in spam.
Stack
Does your organization need a site like this?
I design, build and run websites with their own back end, security and infrastructure.
Related service: Websites & web apps
Contact me