• v1.1.0 696a18ddd3

    zaph0d released this 2026-02-27 02:41:47 +01:00 | 16 commits to main since this release

    📅 Originally published on GitHub: Fri, 27 Feb 2026 01:42:45 GMT
    🏷️ Git tag created: Fri, 27 Feb 2026 01:41:47 GMT

    First successful APK build confirmed ✓

    After deploying to an Intel-based Ubuntu VPS (2 cores, 4 GB RAM), the first complete APK build was produced end-to-end. This release contains the fixes that were required to get there.


    What changed

    Bug fixes

    nginx: dynamic DNS resolution for the backend proxy

    Without a resolver directive, nginx resolves the backend container's hostname once at startup and caches the IP indefinitely. When the backend container restarts (e.g. after an OOM kill), it often receives a new internal Docker IP — nginx keeps hitting the old address, returning 502 Bad Gateway for every /api/* request. The frontend's own healthcheck routes through the same proxy, so it also fails and the container is marked unhealthy.

    Fix: resolver 127.0.0.11 valid=30s ipv6=off + set $backend_upstream http://backend:3001 variable-based proxy_pass. Nginx now re-resolves the hostname on each request (TTL 30 s) so the proxy recovers automatically after a backend restart without any manual intervention.

    SSE keep-alive heartbeat

    nginx and upstream load-balancers close idle connections after their own read/send timeouts. During a Gradle dependency download on a slow link, the SSE stream can be completely silent for minutes — long enough to be killed. The backend now emits an SSE comment line (: heartbeat) every 15 seconds, keeping the stream alive for the full build duration.

    Gradle JVM heap cap (OOM prevention)

    On a 4 GB host running Docker, an unconstrained Gradle JVM can exhaust available RAM. The Linux OOM-killer then terminates the Node.js backend process, the container restarts, and the nginx DNS-caching issue above turns a temporary blip into a permanent outage. GRADLE_OPTS=-Xmx512m -Xms128m is now set by default — sufficient for a standard TWA release build while leaving enough headroom for the host OS and Docker daemon. Override via GRADLE_OPTS in .env if needed.

    Observability improvements

    HTTP request logging (Morgan)

    docker compose logs backend now shows a timestamped line for every inbound request, making it immediately clear whether requests are reaching the backend at all or being rejected at the nginx proxy level.

    Process-level error handlers

    unhandledRejection and uncaughtException are now logged to stdout before the process exits, surfacing crash reasons in docker compose logs backend for post-mortem analysis.


    Files changed

    File Change
    frontend/nginx.conf Add resolver 127.0.0.11, variable proxy_pass, proxy_send_timeout 600s
    backend/src/routes/build.ts SSE keep-alive heartbeat every 15 s
    backend/src/index.ts Morgan request logging, unhandledRejection/uncaughtException handlers
    backend/src/services/builder.ts GRADLE_OPTS=-Xmx512m -Xms128m default
    backend/package.json Add morgan dependency
    README.md Document GRADLE_OPTS, low-memory note, updated feature list
    stats.md Session 4 log, debug entries 13–16, updated cumulative totals

    Upgrading

    git pull
    docker compose up -d --build
    

    The frontend image needs a rebuild (nginx.conf changed). The backend image only needs a docker compose restart backend if you don't want to wait for a full rebuild, but rebuilding is recommended to pick up the Morgan dependency.

    Downloads