What the deploy zip holds
The deploy zip is the artifact a deploy installs and runs: the contents of your application's folder, the folder whose top holds package.json. The turnzero-cloud command builds it from the folder its deploy line runs in. The platform's image build installs what it declares and starts it with npm start on Node.js 24.
What the zip contains
The zip contains:
- the backend as ordinary Node.js code exposing one server that honors
PORT; - the web client's built static assets, where there is one;
- the
migrations/folder, where the application has a database; - the
lib/folder with the library packages the application declares asfile:dependencies; manifest.json, the project's copy, optional, which no deploy runs under, because the platform keeps the onesubmit_manifestrecorded. A copy that differs getsmanifest_notice, a warning that the deploy runs under the recorded manifest, listing the members that differ. The notice also lists eachlib/<name>/package.jsonwhoseturnzero.entryandversionmatch no entry of the recordedpackages, and each the deploy left unread or could not compare, even withoutmanifest.jsonin the zip. Amanifest.jsoncopy larger than 1 MiB or nested deeper than 64 levels is refused (below);- the package manifests,
package.jsonand its lockfile.
A file the list does not name, such as a test/ folder, is still deployed: the image build copies it into the image unchanged, and it runs only where your code loads it.
The zip's root is the folder containing package.json and its lockfile. Where there is anything to install, the image build runs npm install --omit=dev there and links the copied library packages among the dependencies. It then runs npm start, so the start script is the entry point.
What the command leaves out
The command leaves out every node_modules and .git entry and every .env and .env.* file, wherever it stands and whatever the case of its name. So dependencies and environment files stay on your machine. It leaves out nothing else, because a file left out may be one your code loads at run time.
hash --list lists each file the zip would hold, and hash and deploy print the paths left out. They warn of a .zip file at the folder's top, which the zip includes, so keep a build's zip elsewhere. They also warn of a file that looks like a key or a credential. What it prints gives those lines.
The two project layouts
The command zips the folder it runs in, so the folder you run its lines in decides what the zip holds:
- With an
app/folder. Where the application sits inapp/beside the project's library folder,system/, run the command's lines inapp/, so the zip contains the contents ofapp/alone and leavessystem/out. - With no
app/folder. In a project with noapp/folder,system/sits besidepackage.jsonand rides the zip. The image build installs frompackage.jsonand the copies underlib/, never fromsystem/.
Either way, the zip includes the application's own copy of each package under lib/, which package.json declares as a file: dependency (Use the library).
Build a database-backed service lays out such a project, shown here without the contents of system/. The zip contains the contents of app/ and leaves out node_modules, .git, and every .env and .env.* file wherever it stands, the .env file the provision line writes for a local run among them. app/ is the zip's root, containing package.json and its lockfile, manifest.json, and src/main.mjs, the entry its start script runs:
notes-service/
system/ the library entries as taken, never zipped
app/ the zip's root
package.json
package-lock.json
manifest.json
.gitignore
AGENTS.md
migrations/0001_notes.sql
src/db.mjs
src/server.mjs
src/main.mjs
test/notes.test.mjs
lib/database/ the package's package.json and lib/, copied from system/
Names and limits
Every entry of the zip is a relative path inside its root, with / between its segments, and a folder entry such as zip -r writes becomes a folder. A deploy refuses an entry that is absolute, contains a backslash or a NUL, names a drive, or leaves the root through .., since the image build cannot write it.
A deploy refuses a zip naming one path as both a file and a folder the same way, and a name longer than 4,096 bytes or deeper than 128 segments. Each is refused artifact_layout_invalid, with a detail naming the entry, and for a backslash the tar.exe command of step 3 of Deploy an application. So is a zip of more than 65,535 entries, 500,000 name segments in all, or 128 MiB declared, its folder entries counted, and one whose root manifest.json nests deeper than 64 levels.
Bytes that are not a zip are refused artifact_unreadable. A zip with no package.json at its root, or whose package.json is not a JSON object, is refused artifact_layout_invalid. So is one with neither a start script nor a root server.js, since npm start then has nothing to run, and one whose root package.json or manifest.json is larger than 1 MiB. Where exactly one top-level folder of the zip contains a package.json, the detail names that folder: zip its contents instead.
A zip whose root package.json or manifest.json, or a nested package.json the deploy reads, inflates past the size its entry declares is refused artifact_unreadable too. The image build stops at any entry that does, naming it.
The deploy reads the zip before it adds a version, so none of these refusals counts against the day's deploys. Windows PowerShell's Compress-Archive writes \ between segments, which these rules refuse, so a zip you build yourself on Windows comes from tar.exe.
What the image build installs
The install runs your runtime dependencies alone. Development dependencies are never installed, whatever your .npmrc says. Your .npmrc still reaches the install for everything else, such as a private registry.
The image build removes the install scripts, such as postinstall and prepare, from your root package.json and from each workspace member's, one whose folder your root package.json's workspaces match. The image contains those copies. Every other dependency runs its own install scripts as npm runs them, a lib/ package declared as a file: dependency among them. The shell those scripts run under is the build's own.
A workspaces pattern may use literal segments, * within a segment, and ** segments, with each ! pattern after every other and none matching a positive pattern's own text. A * never matches a folder name's leading ., and a ** never passes through a folder whose name opens with ., so packages/* and ** skip .cache. A segment that spells the dot itself, such as .c*, still matches it.
A deploy refuses any other pattern by name, such as one using ?, braces, brackets, parentheses, a backslash, a leading #, or a .. segment. It also refuses a pattern longer than 4,096 bytes or deeper than 128 segments, the bound on an entry's name, and more than 256 patterns or 5,000,000 matching steps.
The image build skips npm install where it would install and run nothing: package.json declares no dependencies other than devDependencies, and no workspaces. The skip also needs a zip root containing no node_modules or .npmrc, and a package.json with no os, cpu, libc, or devEngines field, which npm's install checks. A skipped install changes nothing else: the image still starts with npm start.
Native modules
No binding.gyp of the root's or of a workspace member's is built, so such a native module ships prebuilt. A deploy refuses each one that ships no .node file, as artifact_layout_invalid, naming the entry. The root's ships its .node anywhere in the zip outside node_modules and the members' folders. A member's ships it in its own folder, less any member nested inside it. A lib/ package's binding.gyp builds on the platform's Linux builders, as npm builds it.
Build each .node file your zip ships for Linux, the platform's target. The deploy checks only that it exists; one built for Windows or macOS fails when your application loads it.
The deploy's notice
A deploy returns artifact_notice, listing each install script and binding.gyp of the root's or a workspace member's that the build will not run or rebuild. The deploy reads a package.json below the root only up to 64 KiB, and at most 50 of them. The notice lists each workspace member's it left unread, which the build strips all the same. Where one builds something your application needs, run it locally and ship its output in the zip.
Related
- Deploy an application runs the deploy that sends this zip, and its step 3 gives the
tar.exeline for a zip you build on Windows. - The turnzero-cloud command builds the zip and prints what it left out.
- Use the library copies a library package into
lib/and declares it. - Serve a web client says which files a web client ships in the zip.
- The settings a deployed copy receives describes the image the zip runs on.
- Deploys starts from a refused or failed deploy.