Preparing a Replit app for production requires configuring the .replit file with separate build and run commands, binding your server to 0.0.0.0 instead of localhost, setting up deployment secrets independently from workspace secrets, and ensuring your homepage responds within 5 seconds for health checks. Choose Autoscale for variable traffic, Reserved VM for always-on apps, or Static for frontend-only projects. These steps prevent the most common deployment failure: 'an open port was not detected'.
How to Prepare Replit Apps for Production Deployment
Your app works perfectly in the Replit workspace, but deploying it to production introduces new requirements that trip up most beginners. Health checks, port binding, deployment secrets, and build optimization all need explicit configuration. This tutorial walks you through every step from workspace to published production app, covering the most common errors and how to prevent them.
Prerequisites
- A Replit Core, Pro, or Enterprise account
- A working web app or API that runs correctly in the workspace
- Basic understanding of the .replit configuration file
- Any API keys or secrets already added in the workspace Secrets tool
Step-by-step guide
Configure the deployment section in .replit
Configure the deployment section in .replit
Open the .replit file (enable Show hidden files in the file tree if needed). Add a [deployment] section with separate build and run commands. The build command runs once during deployment to compile your app. The run command starts your server in production. These are separate from the workspace run command, which only applies during development. Set deploymentTarget to cloudrun for Autoscale deployments.
1[deployment]2build = ["npm", "run", "build"]3run = ["node", "dist/index.js"]4deploymentTarget = "cloudrun"Expected result: Your .replit file has a [deployment] section with explicit build and run commands.
Bind your server to 0.0.0.0 on the correct port
Bind your server to 0.0.0.0 on the correct port
The most common deployment error is 'hostingpid1: an open port was not detected'. This happens because your server listens on localhost or 127.0.0.1, which is only accessible from within the container. Change your server to bind to 0.0.0.0 so it accepts connections from outside. Also ensure your port mapping is correct: your app should listen on a local port (like 3000) and .replit maps it to external port 80.
1// Express.js — correct production binding2const express = require('express');3const app = express();45const PORT = process.env.PORT || 3000;6const HOST = '0.0.0.0'; // Required for Replit deployments78app.get('/', (req, res) => {9 res.send('OK');10});1112app.listen(PORT, HOST, () => {13 console.log(`Server running on ${HOST}:${PORT}`);14});Expected result: Your server binds to 0.0.0.0 and Replit's health check can reach it on the configured port.
Set up port configuration in .replit
Set up port configuration in .replit
The [[ports]] section in .replit maps your application's local port to the external port that users access. External port must be 80 for web apps. If your app runs on port 3000 locally, configure the mapping so that external traffic on port 80 routes to your local port 3000. Without this mapping, the deployment health check cannot find your app and fails.
1[[ports]]2localPort = 30003externalPort = 80Expected result: Port 80 external traffic correctly routes to your application's local port.
Add deployment secrets
Add deployment secrets
Workspace secrets do not automatically carry over to deployments. This is the number one cause of deployment failures. Open the Deployments pane in your Replit workspace, find the Secrets section, and add every environment variable your production app needs. This includes DATABASE_URL (auto-configured if using Replit's PostgreSQL), API keys, and any other credentials. Static deployments do not support secrets at all.
Expected result: All required environment variables are configured in the deployment secrets, and your production app will not crash due to undefined values.
Ensure your homepage responds within 5 seconds
Ensure your homepage responds within 5 seconds
Replit's health check requests your homepage (/) after deployment. If it does not respond within 5 seconds, the deployment fails. Heavy initialization logic, slow database connections, or missing environment variables can all cause timeouts. Move expensive startup work to background tasks and make your root route respond immediately. For API servers without a homepage, add a simple health check endpoint at / that returns a 200 status.
1// Health check endpoint for API servers2app.get('/', (req, res) => {3 res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });4});56// If your app has heavy initialization, do it after the server starts7app.listen(PORT, HOST, () => {8 console.log(`Server ready on ${HOST}:${PORT}`);9 // Start heavy initialization after server is listening10 initializeDatabase().catch(console.error);11 warmCache().catch(console.error);12});Expected result: Your homepage responds in under 5 seconds and the deployment health check passes.
Choose the right deployment type and publish
Choose the right deployment type and publish
Replit offers four deployment types. Autoscale scales to zero when idle (15-minute timeout) and charges per usage — best for web apps with variable traffic. Reserved VM stays always-on — best for WebSocket apps and background jobs. Static hosts HTML/CSS/JS only — free but no backend support. Scheduled runs on a cron schedule — best for periodic tasks. Click the Publish button, select your deployment type, configure CPU and RAM, and publish. For teams working on complex production deployments, RapidDev can help optimize architecture and deployment configuration.
Expected result: Your app is published and accessible at your .replit.app URL, passing the health check and serving requests.
Complete working example
1# Production-ready .replit configuration23entrypoint = "src/index.js"45# Development settings6onBoot = "npm install --prefer-offline"7run = ["npm", "run", "dev"]89hidden = [".config", "node_modules", "package-lock.json", "dist"]1011[nix]12channel = "stable-24_05"13packages = ["nodejs-20_x"]1415# Port mapping: local 3000 → external 8016[[ports]]17localPort = 300018externalPort = 801920# Deployment configuration21[deployment]22build = ["npm", "run", "build"]23run = ["node", "dist/index.js"]24deploymentTarget = "cloudrun"2526# Development environment variables (not secrets)27[run.env]28NODE_ENV = "development"29PORT = "3000"3031# Note: Production secrets must be added separately32# in the Deployments pane → Secrets section.33# Do NOT put API keys in this file.Common mistakes when preparing Replit apps for production
Why it's a problem: Server binds to localhost instead of 0.0.0.0 causing 'an open port was not detected' error
How to avoid: Change the host parameter to '0.0.0.0' in your server's listen call
Why it's a problem: Workspace secrets are not added to the deployment configuration causing undefined environment variables
How to avoid: Open the Deployments pane, go to the Secrets section, and add every required environment variable
Why it's a problem: Homepage takes longer than 5 seconds to respond, causing the health check to timeout
How to avoid: Add a lightweight / endpoint and defer heavy initialization to after the server starts
Why it's a problem: Using Static deployment for an app with a backend server
How to avoid: Static deployments only serve HTML/CSS/JS files. Use Autoscale or Reserved VM for apps with backends
Why it's a problem: Expecting the deployed file system to persist between publishes
How to avoid: The deployment file system resets on every publish. Use PostgreSQL, Object Storage, or an external service for persistent data
Best practices
- Always bind your server to 0.0.0.0, never to localhost or 127.0.0.1
- Configure deployment secrets separately from workspace secrets — they do not sync automatically
- Add a health check endpoint at / that responds with 200 in under 5 seconds
- Use separate build and run commands in the [deployment] section to avoid rebuilding on every restart
- Set deploymentTarget to cloudrun for Autoscale or use the Deployments pane to select other types
- Move heavy initialization logic to run after the server starts listening, not before
- Test your production build locally with npm run build && node dist/index.js in Shell before deploying
- Monitor deployment logs in the Deployments pane Logs tab to catch runtime errors after publishing
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
My Replit deployment fails with 'hostingpid1: an open port was not detected'. My Express server uses app.listen(3000). What do I need to change in both my server code and .replit configuration to fix this?
Configure my Replit project for production deployment. Update the .replit file with a [deployment] section including build and run commands. Make sure the Express server binds to 0.0.0.0 and port 3000, add a health check endpoint at /, and set up the port mapping for external port 80.
Frequently asked questions
Your server is binding to localhost or 127.0.0.1 instead of 0.0.0.0. Change the host parameter in your server's listen call to '0.0.0.0' and ensure the [[ports]] section in .replit maps your local port to external port 80.
No. Workspace secrets and deployment secrets are separate. You must add each secret in the Deployments pane under the Secrets section. This is the number one cause of deployment failures.
Autoscale scales to zero after 15 minutes of inactivity and charges per usage, with cold starts of 10-30 seconds. Reserved VM stays always-on with predictable monthly cost. Use Autoscale for web apps with variable traffic and Reserved VM for WebSocket connections, background jobs, or apps that cannot tolerate cold starts.
The health check requests your homepage (/) and waits 5 seconds for a response. If it times out, the deployment fails. Add a lightweight route at / and defer heavy initialization.
No. Static deployments only serve HTML, CSS, and JavaScript files. They do not support server-side code, backends, or secrets. Use Autoscale or Reserved VM for apps with backend logic.
Autoscale has a $1/month base fee plus compute charges and $0.40 per million requests. A small web app with 50 visitors per day costs roughly $1.05/month. A busier API with 10,000 calls per day costs roughly $14.27/month.
Yes. Deployments on the free Starter plan expire after 30 days. You need a Core ($25/month) or higher plan for persistent deployments.
No. The file system resets every time you publish a new version. Store persistent data in Replit's PostgreSQL database, Object Storage, or an external service.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation