Partitions and the flash layout
How the firmware lays out flash: a partition table generated per build from the board's fixed spec plus an auto-sized storage image, what a board definition pins (flash size and — critically — the PSRAM mode), and why small, cheap boards are the tight-fit case to watch.
The firmware image, the PHP source, and any persistent data all live in the chip's flash, carved into partitions. php-esp32 does not ship one fixed table: it generates it per build, so the same source adapts to a microSD project (source on the card) or an embedded one (source packed into flash), and to boards with very different flash sizes.

A ~$4, fingertip-sized ESP32-S3 (4 MB flash / 2 MB PSRAM). On boards this small the ~3 MB firmware, the embedded source, and the store all have to fit at once — this page is about making that work.
How the table is built
cmake/gen-partitions.cmake runs before the build and assembles the table from two layers:
| Layer | Partitions | Source |
|---|---|---|
| Fixed | nvs, phy_init, factory (the app) |
the board's committed boards/<family>/<board>/partitions.csv |
| Generated | storage (embedded source FAT), phpstore (persistent store_* NVS) |
sized and appended per build |
storageis added only for an embedded project, sized to the PHP source (cluster-accurate occupancy + FAT overhead +[storage] reserve_kb). A microSD project has nostoragepartition — the source runs from the card andmain.cfalls back to it.phpstoreis added only when the project sets[store] size_kb(see Persistent store).
The generated CSV lands in build/partitions.gen.csv (never committed) and is selected via a
sdkconfig fragment layered last, so it wins over the board's committed default.
What a board definition pins
A board is a directory under boards/<family>/<board>/. Two files decide the flash layout:
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # how much flash the table must fit inside
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
CONFIG_SPIRAM_MODE_QUAD=y # PSRAM electrical mode (Quad vs Octal) -- see below
partitions.csv— the fixed partitions, above all thefactorysize that caps how big the firmware can be (and, on a small board, how much is left forstorage).sdkconfig.board— layered last over the base and the family (boards/<family>/sdkconfig.family), so board choices win. It pins the flash size and, when the board differs from its family, the PSRAM mode.
PSRAM mode is not cosmetic — it decides whether PHP runs at all
The build sets USE_ZEND_ALLOC=0, so the entire Zend runtime heap lives in PSRAM. If PSRAM does not
initialise, the engine has no memory and the board never gets past boot. PSRAM comes in Quad and
Octal electrical modes, and a module only speaks one. The ESP32-S3-ETH/Pico carry Octal PSRAM (the
family default); the tiny ESP32-S3-Zero carries Quad. Pinning the wrong mode is a silent failure —
phpflash discover reads the real chip so you can set the right one.
Small-flash boards: the tight-fit case
The board in the photo — an ESP32-S3-Zero (ESP32-S3FH4R2) — has 4 MB flash and 2 MB Quad PSRAM, far less than the S3-ETH's 16 MB / 8 MB Octal. Its board definition overrides the family assumptions:
| Setting | Family default (S3-ETH) | ESP32-S3-Zero |
|---|---|---|
| Flash | 16 MB | 4 MB |
| PSRAM | Octal, 8 MB | Quad, 2 MB |
factory |
10 MB | 3456K (app is ~2.8 MB; leaves room for storage) |
With those three lines the ~2.8 MB firmware, a small embedded storage FAT, and nvs/phy_init all
fit inside 4 MB, and 2 MB of PSRAM is enough for a small script. (A framework the size of Symfony needs
far more heap than 2 MB, so it belongs on a big-PSRAM board — this is a memory limit, not a bug.)
Bringing up or fixing a board
When you meet a board whose flash or PSRAM differs from what its definition assumes, don't guess:
-
01
Read the real chip
phpflash discoverprints the true flash size and PSRAM (e.g.Embedded Flash 4MB (XMC), Embedded PSRAM 2MB (AP_3v3)). -
02
Pin flash size and PSRAM mode
in
sdkconfig.board(CONFIG_ESPTOOLPY_FLASHSIZE*, andCONFIG_SPIRAM_MODE_QUAD/_OCTwhen it differs from the family). -
03
Size `factory`
in the board's
partitions.csvso the firmware fits and leaves room for the generated partitions on that flash.
The esp32-s3-zero board is exactly this correction applied — a good template for a new small board.
Overriding it per project
The fixed table belongs to the board, but a single project can take it over without forking the
firmware: drop a partitions.csv next to php-esp32.config.toml and the build uses it instead (the
generated storage/phpstore are still appended). phpflash partitions publish writes a starter
table from the board's defaults, with a per-board list of sensible factory sizes — see the phpflash
docs. The custom-partitions example shows it end to end.