Deploying apps
Framework guides
What is detected for each stack and what has to be provided.
On this page
Next.js
next dependency in package.json, then built and served, standalone output included. Next reads PORT on its own, so there is usually nothing to change.
Vite, React and Vue
vite, react-dom, react-scripts or vue in package.json. These build to static files, which are served directly: fast, and with no Node process to keep alive.
dist or build. Never set vite dev or npm start as the production command for a static site. A dev server binds localhost only and is not built for production traffic.
Angular
angular.json and built to static output. Angular Universal, or any SSR mode, produces a Node server instead, which has to read PORT and bind 0.0.0.0 like any other Node app.
SvelteKit
npm create svelte ships with @sveltejs/adapter-auto. That adapter picks its output by looking for environment variables set by specific hosts. On a general-purpose container host those variables are absent, so it cannot pick a target and produces a build with no runnable server. Switch to the Node adapter.
npm install @sveltejs/adapter-nodenpm uninstall @sveltejs/adapter-auto
svelte.config.js and add a start script.
// svelte.config.jsimport adapter from '@sveltejs/adapter-node';// package.json"scripts": { "start": "node build" }
adapter-node binds 0.0.0.0 and reads PORT by default, so once it is in place the health check can reach the app.
Node.js
package.json, and the start script is used if there is one. Express, Fastify, Koa, NestJS and Hapi all work. The only requirement is that the server reads PORT and binds 0.0.0.0.
Python: Django, Flask and FastAPI
requirements.txt or pyproject.toml, with the framework identified from the dependencies and from manage.py for Django. Serve with a production server, gunicorn or uvicorn, bound to the port the container is given.
# Django or Flask (WSGI)gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT# FastAPI (ASGI)uvicorn main:app --host 0.0.0.0 --port $PORT
manage.py runserver or flask run in production. Both are development servers, and runserver binds localhost by default. For Django, set ALLOWED_HOSTS to include the app’s domain, or every request returns a 400.
Spring Boot
pom.xml or a Gradle build file containing Spring. Spring Boot defaults to port 8080, so point it at the one it is given, either in application.properties or with a flag at startup.
# application.propertiesserver.port=${PORT:8080}server.address=0.0.0.0
PHP and Laravel
composer.json, from the artisan file for Laravel, or from a bare index.php. Laravel apps are served through a web server, so the port does not need managing by hand. Set APP_KEY as an environment variable, and remember that migrations do not run on their own: run them from the app once the database is connected.
Static sites
index.html and no framework is served as it is. This is the cheapest and most reliable thing to host: no process, nothing to health check, nothing to restart.
Anything else
Dockerfile takes precedence and is built instead, which is the escape hatch for anything unusual.
PORT, bind 0.0.0.0, and never ship a development server as the production command. Almost every framework-specific problem comes back to one of those three.