The Difference Between What a Framework Says It Needs and What It Actually Needs to Run in Production
Framework documentation is written for developers, not for servers. The gap between "how to get started" and "how to run this in production" is where most first deployments fail.
Framework documentation is written to get you started as quickly as possible. It shows you how to install the package, scaffold a project, and run a development server. Within a few minutes you have something working.
What it does not show you, or shows you buried in a section called "Deployment" that most people never read, is what that framework actually needs to run on a server somewhere.
This gap is where most first deployments fail.
What "it just works" means in development
Modern frameworks do a lot of invisible work during local development. A dev server watches your files for changes and rebuilds automatically. TypeScript is compiled on the fly. Environment variables are loaded from a .env file automatically. Static assets are served directly from the source directory. Database connections are established once and reused across requests without any explicit configuration.
None of this is wrong. It is genuinely useful. The problem is that it creates a mental model of the framework as something that just works without much configuration. When you try to run the same app in production, none of those conveniences exist.
The build step that nobody mentions upfront
Most JavaScript and TypeScript frameworks require a build step before the app can run in production. The development server handles this invisibly. For production, you need to run npm run build or the equivalent, which compiles TypeScript, bundles JavaScript, optimizes assets, and produces output that a production server can actually run.
This seems obvious once you know it. It is not obvious when you are building your first app with an AI tool that never brought it up.
Frameworks differ significantly in what this build step produces and where the output goes. Next.js puts it in .next. Vite puts it in dist. AdonisJS puts it in build. If you point your deployment platform at the wrong directory, or run the wrong start command, the app will not start.
The environment variables a framework needs that are not in your .env
Beyond the environment variables your app code reads directly, many frameworks read their own environment variables that control behavior in production. These are rarely covered in getting started guides.
A Node.js app should run with NODE_ENV=production. This is not just a convention. Many frameworks and libraries check this value and change their behavior accordingly. Express disables verbose error messages. React skips development warnings. Some ORMs change their connection pool settings. If you deploy without setting this, you are running a production app in development mode, which is slower, more verbose, and sometimes insecure.
Similarly, NODE_ENV=production tells many packages to skip installing development dependencies, which makes builds faster and containers smaller. Missing it means deploying with packages that have no business being on a production server.
The start command is not the dev command
The command that starts your app in development (npm run dev, vite, ts-node src/index.ts) is almost never the right command for production.
Development servers include hot reload, watch mode, and TypeScript compilation at runtime. These features add overhead and are designed for a developer sitting at a keyboard, not for a server handling requests. Running your dev command in production works, in the sense that the app starts and responds to requests, but it runs slower, uses more memory, and behaves unpredictably under load.
The correct production start command runs the compiled output: node dist/index.js, node build/server.js, or whatever your build process produces.
Ports and binding addresses
Frameworks default to binding on localhost, which means they only accept connections from the same machine. On a local development machine this is fine. On a server, your app needs to accept connections from outside. The binding address should be 0.0.0.0.
Similarly, frameworks default to a specific port: 3000, 8080, 4000 depending on the framework. Hosting platforms assign the port dynamically through the PORT environment variable. If your app ignores PORT and listens on a hardcoded value, the platform cannot route traffic to it.
Most framework documentation mentions this. Most getting started guides do not.
Static assets in production
Many frameworks serve static assets through the dev server during development. In production, serving static files through Node.js works but is inefficient. The standard approach is to put a web server or CDN in front that handles static assets directly.
Some frameworks generate a public directory that should be served separately. Others bundle assets into the build output. If you deploy without understanding how your framework handles static files, you may end up with a working app that loads slowly because every asset request goes through Node.js, or a broken app because assets are expected at a path that does not exist.
What Jetpacked does with this
Jetpacked knows the production requirements for the frameworks it supports, not just how to scaffold them, but what they actually need to run on a server. The build command, the start command, the environment variables that need to be set, the port configuration, how static assets should be handled.
When you connect a repository, Jetpacked identifies the framework, applies the correct production configuration, and fills in what the documentation leaves out. The goal is that you should not need to read the deployment section of every framework's documentation to get your app running.
Deploy your app in minutes
Jetpacked handles Docker, HTTPS, databases, and deployments so you can focus on building.
Launch your app free