DEV Community

Иван Петров
Иван Петров

Posted on

I host a 4,000-page website on a Raspberry Pi 5 with 1 GB of RAM

 Caissa Codex (https://chesscodex.org) is a free encyclopedia of chess openings: 3,690 named lines, each with its own page, plus pages for every ECO code and a few rankings, about 4,000 URLs in total. It runs on a Raspberry Pi 5 with 1 GB of RAM in my home. Here is what makes that work.

Boring stack, on purpose

PHP 8.4 and SQLite behind nginx and PHP-FPM. No framework, no build step, no Node on the server. The front controller is one file with a route table, the views are plain PHP templates, and a small PHP script minifies the CSS and JavaScript. A page is a few prepared SQLite queries and a template, which a Pi handles easily.

Let the edge do the work

Anonymous pages send Cache-Control: public, s-maxage=600, and Cloudflare keeps them for 10 minutes. Most requests never reach the Pi, and PageSpeed scores stay around 100.

No open ports

The Pi is reachable only through a Cloudflare Tunnel: the router has no port forwarding, and SSH goes through Cloudflare Access with keys only.

Living with a rate-limited API

The statistics come from the Lichess opening explorer, which in practice allows about 20 requests a minute. So the site never fans out requests: the numbers are cached in SQLite and refreshed in the background one request at a time, and bulk data (results by rating band for the 1,500 most-played lines) is collected by slow command-line jobs.

Stockfish on the Pi

Every line's final position was evaluated by Stockfish 19 at depth 30 on the same Pi, and the results sit in a table next to the statistics. That makes comparisons like this possible: Stockfish gives White +2.03 after the Stafford Gambit, yet Black scores 50.8% in 4.6 million Lichess games.

Security basics

A strict Content Security Policy without inline scripts (all JavaScript lives in files), escaped output everywhere, and prepared statements for every query.

Top comments (1)

Collapse
 
mrsaynothing profile image
Mr Say Nothing •

We run the same tunnel, no-open-ports shape on a bigger box, and the never-fan-out rule is the part people skip — one request at a time against a rate-limited upstream beats the burst-and-429 cycle every time. What does a cold page cost you on 1 GB — is the SQLite cache small enough to stay page-cached, or do the first hits after a restart hit disk?