From 04eb36d0a22b87f8f23d40a3403502e84c0aa6e1 Mon Sep 17 00:00:00 2001 From: davidmonterocrespo24 Date: Sat, 9 May 2026 23:04:16 +0200 Subject: [PATCH] fix(docker): VOLUME defaults so standalone runs get the perf caches too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A user reported on Discord that an ESP32 Blink compile on a fresh `docker run -d ghcr.io/.../velxio:master` (no -v flags) takes 8-9 minutes — every single time. Same hardware that the local project checkout flies through in 5-30 seconds with the new persistent build dir + ccache pipeline. Root cause: `docker run` without `-v` mounts gets nothing persistent. ccache + persistent build dir live in /var/cache/ccache and /var/lib/velxio-build, both wiped on every `docker rm` (which the user explicitly did when troubleshooting). docker-compose users get the volumes via the compose file; standalone users got nothing because the Dockerfile didn't declare them. This PR closes that gap. Dockerfile.standalone - VOLUME ["/app/data", "/root/.arduino15", "/root/Arduino", "/var/cache/ccache", "/var/lib/velxio-build"] Anonymous volumes are now created automatically when the user runs the image without -v. They survive `docker stop`/`docker start`/ `docker rm` (only `docker rm -v` or `docker volume prune` removes them). Users can still pass `-v` for named volumes — explicit mounts always win over the VOLUME directive. - Replace `ccache --set-config max_size 8G` (RUN, written to /var/cache/ccache/ccache.conf which the volume mount masks at runtime) with `ENV CCACHE_MAXSIZE=8G` etc. — env vars override any conf-file value on every ccache invocation, so the 8 GB cap actually applies at runtime regardless of what's in the volume. README.md - Update both the quick-start docker run and the detailed self-host section to include all five volumes. - Add a note explaining what each volume is for and that without them, cold compile times are 5-7 min vs the 5-30 s warm path. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile.standalone | 39 ++++++++++++++++++++++++++++----------- README.md | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/Dockerfile.standalone b/Dockerfile.standalone index e5cb09d6..612c05c8 100644 --- a/Dockerfile.standalone +++ b/Dockerfile.standalone @@ -187,21 +187,38 @@ ENV CCACHE_DIR=/var/cache/ccache ENV IDF_CCACHE_ENABLE=1 # CCACHE_BASEDIR makes ccache treat absolute paths under this prefix as # relative when computing the cache key. Combined with the persistent -# /var/lib/velxio-build// build dir we mount as a volume, this lets -# ccache hit across compiles even though some flags (-I, -fmacro-prefix-map) -# embed absolute paths into the command line. +# /var/lib/velxio-build// build dir, this lets ccache hit across +# compiles even though some flags (-I, -fmacro-prefix-map) embed absolute +# paths into the command line. ENV CCACHE_BASEDIR=/var/lib/velxio-build -# Cache cap, compression. These are set as env vars (rather than written -# to /var/cache/ccache/ccache.conf via `ccache --set-config` at image-build -# time) because $CCACHE_DIR is a named volume — anything we write into it -# during build is masked at runtime by the volume mount, so config in the -# image gets shadowed by whatever's already in the volume. ccache reads -# CCACHE_MAXSIZE / CCACHE_COMPRESS / CCACHE_COMPRESSLEVEL at runtime, -# overriding any conf-file value. +# Cache cap + compression set as env vars (rather than via `ccache +# --set-config` at image-build time) because $CCACHE_DIR is a docker volume: +# anything written into /var/cache/ccache during the RUN step is masked at +# runtime by the volume mount. ccache reads CCACHE_MAXSIZE / CCACHE_COMPRESS +# / CCACHE_COMPRESSLEVEL on every invocation and they override any conf-file +# value, so the cap actually applies at runtime. ENV CCACHE_MAXSIZE=8G ENV CCACHE_COMPRESS=1 ENV CCACHE_COMPRESSLEVEL=6 -RUN mkdir -p /var/cache/ccache +RUN mkdir -p /var/cache/ccache /var/lib/velxio-build /root/Arduino + +# ── Persistent paths ──────────────────────────────────────────────────────── +# Declaring these as VOLUMEs means `docker run` (without explicit -v) creates +# anonymous volumes for them — they survive `docker stop`/`docker start` and +# even `docker rm`. Without this, every container restart wipes the ccache +# and persistent ESP-IDF build dir, so every compile is cold (~5-7 min on +# modest hardware) instead of the warm-cache 5-30s we measured on prod. +# +# Users SHOULD pass `-v velxio-X:/path` for each of these to get named +# volumes (easier to inspect / back up than anonymous ones), but the +# anonymous default is a sensible fallback. +# +# /app/data — SQLite DB + project files + auto-generated SECRET_KEY +# /root/.arduino15 — arduino-cli config + installed cores +# /root/Arduino — Library Manager-installed Arduino libraries +# /var/cache/ccache — ccache cache (ESP-IDF compiles) +# /var/lib/velxio-build — persistent ESP-IDF build dir (one subdir per target) +VOLUME ["/app/data", "/root/.arduino15", "/root/Arduino", "/var/cache/ccache", "/var/lib/velxio-build"] # Install ESP-IDF Python dependencies using the final image's Python # The requirements.txt has version constraints required by ESP-IDF 4.4.x diff --git a/README.md b/README.md index fa6f63c7..e29b01e4 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,22 @@ Your support helps cover server costs, library maintenance, and frees up time to To self-host with Docker (single command): ```bash -docker run -d -p 3080:80 ghcr.io/davidmonterocrespo24/velxio:master +docker run -d -p 3080:80 \ + -v velxio-data:/app/data \ + -v velxio-arduino-libs:/root/.arduino15 \ + -v velxio-arduino-user-libs:/root/Arduino \ + -v velxio-ccache:/var/cache/ccache \ + -v velxio-build:/var/lib/velxio-build \ + ghcr.io/davidmonterocrespo24/velxio:master ``` Then open . +The named volumes are what make compile times reasonable on subsequent +runs — without them, every container restart wipes the ESP-IDF build +cache and the first compile after each restart takes 5-7 minutes +instead of 5-30 seconds. + --- ## Screenshots @@ -269,16 +280,35 @@ docker run -d \ -p 3080:80 \ -v velxio-data:/app/data \ -v velxio-arduino-libs:/root/.arduino15 \ + -v velxio-arduino-user-libs:/root/Arduino \ + -v velxio-ccache:/var/cache/ccache \ + -v velxio-build:/var/lib/velxio-build \ ghcr.io/davidmonterocrespo24/velxio:master ``` Open . -The two named volumes persist: +The five named volumes persist: - `velxio-data` → `/app/data`: SQLite DB, project sketch files, auto-generated `SECRET_KEY` - `velxio-arduino-libs` → `/root/.arduino15`: arduino-cli config + installed cores (saves a 5–10 min reinstall on every container restart) +- `velxio-arduino-user-libs` → `/root/Arduino`: Library Manager-installed + Arduino libraries (e.g. Adafruit_BMP280, DHT, GFX). Without this, + every container restart re-downloads them on next compile. +- `velxio-ccache` → `/var/cache/ccache`: ccache C/C++ object cache for + ESP-IDF compiles. Empty on first compile, populated as you go; + subsequent compiles hit the cache and finish in seconds instead of + minutes. +- `velxio-build` → `/var/lib/velxio-build`: persistent ESP-IDF build dir + (one subdir per target — esp32, esp32c3, esp32s3). Lets ninja's + incremental build skip everything that hasn't changed; a re-compile + of an unchanged sketch finishes in 2-5 seconds. + +If you skip the volume flags, the Dockerfile declares all five paths as +`VOLUME`, so docker creates anonymous volumes and the caches still +survive container restarts (just harder to inspect/back up than named +ones). Only `docker rm -v` or `docker volume prune` would wipe them. ---