Skip to content

v0.3.2.

Complete release notes · 2026-08-26. Read upgrade instructions and runtime limits before deploying.

Added

  • One Runtime Version Catalog: RUNTIME_VERSION_CATALOG in @ninedeploy/schemas is now the single source for every version NineDeploy suggests — the Manifest Creator presets, its version picker and the CLI's starterManifest all read from it, so a bump is one edit instead of three that can disagree. Each entry carries its upstream support state and EOL date, and the catalog records the date it was last reviewed so staleness is visible rather than silent. Tests assert the table's invariants: no recommended pin may be security-only or end-of-life, and every offered version must satisfy the manifest schema.
  • Runtime Versions Are Picked, Not Typed — And Old Ones Stay Available: the Manifest Creator's Version field is a picker listing each version with its support status, plus an "Other version…" escape hatch for anything the catalog does not list. Choosing an end-of-life or security-only version is still allowed — reproducing a legacy runtime is a real need — but it now carries an advisory naming the recommended pin instead of passing silently. Nothing here blocks a build, and runtime.version stays free-form in the schema. Switching runtime type now drops a pin that is meaningless for the new type rather than carrying, say, Node's 24 over to Python.

Fixed

  • The Manifest's Build Half Was Never Applied — And The Docs Said Otherwise: .ninedeploy documented runtime, phases and build as taking effect at build time, and 0.3.0's notes claimed the pipeline "applies the build sections at build time". It does not. engine/pipeline.ts applies only the operational sections (routes, alerts, database); applyManifestToBuildConfig and generateNixpacksToml are both written, tested — and called by nothing. The builder invokes the Nixpacks CLI with --install-cmd/--build-cmd/--start-cmd and writes no nixpacks.toml. The docs, the module comments and the Manifest Creator's Runtime section now say this plainly instead of implying a pin that never happens. Wiring it up needs a real Linux/Docker/Nixpacks integration test and is deliberately not in this release.
  • Every Runtime Pin The Generator Produced Was Broken: generateNixpacksToml built nixpkgs attribute names by stripping non-digits, yielding go_127 and ruby_34 where nixpkgs uses go_1_27 and ruby_3_4, and turning any patch-level pin into nonsense (24.4.1 → nodejs_2441). Worse, [phases.setup] nixPkgs replaces the provider's package list unless it contains the "..." sentinel — so declaring one extra package silently deleted the toolchain, and an unresolvable name is a hard undefined variable Nix error late in docker build. Version pins now go only through the provider environment variables Nixpacks actually reads, every nixPkgs list leads with "..." so extras are additive as documented, and the generator returns warnings instead of emitting a pin it knows will be ignored or will fail.
  • Pins Nixpacks Cannot Honour Are Named, Not Guessed: checked against the v1.41.0 the installer pins, NIXPACKS_GO_VERSION and NIXPACKS_PHP_VERSION do not exist (those versions come from go.mod and composer.json), Node silently falls back to 18 outside {14,16,18,20,22,24}, Python silently falls back to its default outside 3.7–3.13, Ruby and Rust need an exact patch version, and JDK fails the build outside {8,11,17,19,20,21}. The generator refuses each of these with a specific reason rather than emitting something that breaks or quietly builds the wrong thing.
  • Manifest Creator Shipped End-Of-Life Runtimes As Its Defaults: every preset pinned Node 20, Python 3.12 and Go 1.22 — versions that lost upstream support on 2026-04-30, 2025-04-02 and 2025-02, respectively. They were hand-written literals duplicated between apps/web and packages/sdk, so nobody owned them and they drifted until a deployment tool was recommending unpatched runtimes. Defaults are now Node 24 (Active LTS), Python 3.14, Go 1.27, Ruby 3.4, PHP 8.4, Java 25 and Rust 1.98.
  • The Pre-Upgrade Snapshot Usually Did Nothing: the installer passed .data/ninedeploy.db and .data/master.key to a single tar with stderr muted. master.key is written lazily — the first time something is encrypted — so on any instance without stored secrets the file is absent, tar exits non-zero, and the entire snapshot was skipped, database included. The operator saw one vague warning and upgraded with no rollback point, next to a truncated archive left on disk. Each file that exists is now archived (SQLite's -wal/-shm sidecars included so the snapshot is a consistent set), a missing master.key is treated as the normal state it is, tar's actual error is printed instead of muted, and a failed archive is removed rather than left behind.
  • Failure Messages Claimed A Backup That Did Not Exist: every readiness-gate failure ended with "A pre-update backup is in …/upgrade-backups" regardless of whether the snapshot had run. The message now reports what actually happened.

Installer changes take effect immediately — install.sh is fetched from main, not from the release tarball.


Source CHANGELOG.md at 6046cfe8