# How to deploy a Flask app on Replit

- Tool: Replit
- Difficulty: Advanced
- Time required: 25 minutes
- Compatibility: Replit Core and Pro plans (Autoscale deployment requires paid plan)
- Last updated: March 2026

## TL;DR

Deploying a Flask app on Replit requires binding the server to 0.0.0.0, using gunicorn as the production WSGI server, configuring the .replit file with proper deployment commands, and adding all secrets to the deployment configuration separately. Set the port to use the PORT environment variable, configure the deployment target as cloudrun for Autoscale, and ensure the health check endpoint responds within 5 seconds. The most common deployment failure is binding to localhost instead of 0.0.0.0.

## Deploying Flask on Replit: From Development to Production with Gunicorn and Autoscale

Flask is one of the most popular Python web frameworks, and Replit provides built-in hosting to deploy Flask apps without managing servers. However, Flask's built-in development server is not suitable for production. This tutorial walks through setting up a Flask project, replacing the development server with gunicorn, configuring the .replit file for deployment, handling environment-specific settings, and troubleshooting the most common deployment errors. By the end, your Flask app will be live on a Replit Autoscale deployment with proper error handling and health checks.

## Before you start

- A Replit Core or Pro account for production deployments
- Basic Python knowledge and familiarity with Flask routing
- Understanding of how pip and requirements.txt work
- Familiarity with Replit Secrets for storing environment variables
- Knowledge of the difference between development and production servers

## Step-by-step guide

### 1. Create a Flask project with the correct structure

Start a new Python project on Replit or ask Agent to create a Flask web application. Your project needs at minimum an app.py file, a requirements.txt listing Flask and gunicorn, and a .replit configuration file. Organize larger projects with a standard Flask structure using templates/, static/, and a dedicated config module. Install Flask and gunicorn from the Shell with pip install flask gunicorn, then freeze the dependencies to requirements.txt.

```
# In the Shell terminal
pip install flask gunicorn
pip freeze > requirements.txt
```

**Expected result:** Your project has Flask and gunicorn installed, and requirements.txt lists all dependencies with version numbers.

### 2. Write the Flask app with 0.0.0.0 binding

Create your main Flask application file. The critical configuration for Replit is binding the server to 0.0.0.0 instead of the default 127.0.0.1 or localhost. Without this, Replit cannot detect the running server and the deployment fails with 'hostingpid1: an open port was not detected.' Also read the port from the PORT environment variable since Replit assigns it dynamically in production. Add a health check route at the root path that responds quickly, as the deployment health check must complete within 5 seconds.

```
# app.py
import os
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def health_check():
    return jsonify({
        'status': 'ok',
        'environment': 'production' if os.getenv('REPLIT_DEPLOYMENT') == '1' else 'development'
    })

@app.route('/api/hello')
def hello():
    return jsonify({'message': 'Hello from Flask on Replit!'})

if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))
    debug = os.getenv('REPLIT_DEPLOYMENT') != '1'
    app.run(host='0.0.0.0', port=port, debug=debug)
```

**Expected result:** The Flask app starts, binds to 0.0.0.0, and the preview pane shows the health check JSON response.

### 3. Configure gunicorn as the production WSGI server

Flask's built-in server handles one request at a time and is not designed for production traffic. Gunicorn is a production-grade WSGI server that runs multiple worker processes to handle concurrent requests. Create a gunicorn configuration file to set the number of workers, bind address, and timeout. The number of workers should typically be 2 to 4 for Replit's resource constraints. Higher counts consume more RAM and may cause out-of-memory errors.

```
# gunicorn.conf.py
import os

bind = f"0.0.0.0:{os.getenv('PORT', '5000')}"
workers = 2
threads = 2
timeout = 120
accesslog = '-'
errorlog = '-'
loglevel = 'info'
```

**Expected result:** The gunicorn configuration file is saved and ready for use in the deployment run command.

### 4. Configure the .replit file for Flask deployment

Open the .replit file and set up both development and deployment configurations. The run command starts Flask's development server for local testing. The [deployment] section uses gunicorn for production. The build command installs dependencies from requirements.txt. Set the deployment target to cloudrun for Autoscale deployments. Configure the port mapping with localPort matching your Flask port and externalPort set to 80.

```
entrypoint = "app.py"
run = "python app.py"

modules = ["python-3.11:v18-20230807-322e88b"]

[nix]
channel = "stable-24_05"
packages = ["python311", "python311Packages.pip"]

[[ports]]
localPort = 5000
externalPort = 80

[deployment]
build = ["pip", "install", "-r", "requirements.txt"]
run = ["gunicorn", "--config", "gunicorn.conf.py", "app:app"]
deploymentTarget = "cloudrun"
```

**Expected result:** The .replit file has separate dev and production configurations. Clicking Run starts the dev server, and deploying uses gunicorn.

### 5. Add secrets to both workspace and deployment configuration

Store all sensitive values like API keys, database credentials, and secret keys in Replit Secrets. Open Tools > Secrets and add each key-value pair. Flask apps commonly need a SECRET_KEY for session security and any external API keys. After adding workspace secrets, open the Deployments pane and add the same secrets there. This is the most common cause of Flask deployment failures: secrets that work in development return None in production because they were not added to the deployment configuration.

```
# app.py - Reading secrets
import os

app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY', 'dev-key-change-me')

# Validate required secrets at startup
required_secrets = ['FLASK_SECRET_KEY', 'DATABASE_URL']
for secret in required_secrets:
    if not os.getenv(secret):
        if os.getenv('REPLIT_DEPLOYMENT') == '1':
            raise RuntimeError(
                f'{secret} is not set. Add it in Deployments > Secrets.'
            )
```

**Expected result:** All required secrets are configured in both the workspace Secrets panel and the deployment configuration.

### 6. Deploy and troubleshoot common errors

Click the Deploy button in the Deployments pane and select Autoscale. Configure CPU and RAM settings, then click Deploy. Watch the deployment logs for errors. The most common errors are: 'hostingpid1: an open port was not detected' (server not bound to 0.0.0.0 or wrong port), health check timeout (root route takes more than 5 seconds), and 'ModuleNotFoundError' (dependency not in requirements.txt). If the deployment fails, check the Logs tab in the Deployments pane for the exact error message. For complex Flask deployments with multiple services, teams can consult RapidDev for architecture guidance.

**Expected result:** Your Flask app is live on a .replit.app URL. The health check responds with a JSON status message.

## Complete code example

File: `app.py`

```python
# app.py
# Production-ready Flask app for Replit deployment

import os
from flask import Flask, jsonify, request
from functools import wraps

app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY', 'dev-key-change-me')

is_production = os.getenv('REPLIT_DEPLOYMENT') == '1'

# Validate secrets in production
if is_production:
    required = ['FLASK_SECRET_KEY']
    missing = [s for s in required if not os.getenv(s)]
    if missing:
        raise RuntimeError(f'Missing secrets: {", ".join(missing)}')


def log_request(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        app.logger.info(f'{request.method} {request.path}')
        return f(*args, **kwargs)
    return decorated


@app.route('/')
def health_check():
    return jsonify({
        'status': 'ok',
        'environment': 'production' if is_production else 'development',
        'version': '1.0.0',
    })


@app.route('/api/items', methods=['GET'])
@log_request
def get_items():
    try:
        items = [
            {'id': 1, 'name': 'Item One'},
            {'id': 2, 'name': 'Item Two'},
        ]
        return jsonify(items)
    except Exception as e:
        app.logger.error(f'Error: {str(e)}')
        message = 'Internal server error' if is_production else str(e)
        return jsonify({'error': message}), 500


@app.errorhandler(404)
def not_found(e):
    return jsonify({'error': 'Not found'}), 404


@app.errorhandler(500)
def server_error(e):
    return jsonify({'error': 'Internal server error'}), 500


if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))
    app.run(host='0.0.0.0', port=port, debug=not is_production)
```

## Common mistakes

- **Running Flask's built-in server in production instead of gunicorn** — undefined Fix: Flask's development server handles one request at a time and is not secure. Use gunicorn in the [deployment] run command: gunicorn --config gunicorn.conf.py app:app.
- **Binding to localhost or 127.0.0.1 instead of 0.0.0.0** — undefined Fix: Replit requires the server to bind to 0.0.0.0 for the port to be detected. Change app.run(host='0.0.0.0') or set bind = '0.0.0.0:PORT' in gunicorn.conf.py.
- **Hardcoding the port number instead of reading from PORT env var** — undefined Fix: Replit assigns the port dynamically in production. Use port = int(os.getenv('PORT', 5000)) to read the assigned port with a fallback for development.
- **Not including gunicorn in requirements.txt** — undefined Fix: If gunicorn is not in requirements.txt, the deployment build cannot install it. Run pip freeze > requirements.txt after installing gunicorn.
- **Leaving debug=True enabled in production** — undefined Fix: Debug mode exposes stack traces and enables the interactive debugger, which is a security risk. Disable it in production: debug = os.getenv('REPLIT_DEPLOYMENT') != '1'.

## Best practices

- Always bind Flask to 0.0.0.0, never localhost or 127.0.0.1, for Replit deployments
- Use gunicorn with 2 to 4 workers for production instead of Flask's built-in development server
- Read the port from the PORT environment variable since Replit assigns it dynamically
- Disable debug mode in production using the REPLIT_DEPLOYMENT environment variable
- Freeze dependencies with pip freeze > requirements.txt after every package installation
- Add all secrets to both workspace Secrets and the deployment configuration separately
- Create a health check endpoint at the root path that responds in under 5 seconds
- Validate required secrets at startup with clear error messages in production

## Frequently asked questions

### Why does my Flask app work locally but fail when deployed?

The most common cause is missing secrets. Workspace secrets do not transfer to deployments. Add every secret your app needs in the Deployments pane configuration. The second most common cause is binding to localhost instead of 0.0.0.0.

### What does 'hostingpid1: an open port was not detected' mean?

This error means Replit's health check could not find a listening server. The server is either bound to localhost instead of 0.0.0.0, using the wrong port, or the process crashed before starting to listen.

### How many gunicorn workers should I use?

Start with 2 workers for the Core plan (8 GiB RAM). Each worker duplicates the app in memory. Monitor RAM usage in the Resources panel and increase workers only if you have headroom. Going above 4 workers on Replit is rarely beneficial.

### Can I use Flask with a React frontend on the same Replit?

Yes. Serve your React build output from Flask's static file handling or use Flask as an API backend with React running on a separate port. Configure both ports in the .replit file.

### Do I need to pay for hosting a Flask app on Replit?

The Starter plan supports development but production deployments require Core ($25/month) or Pro ($100/month). Autoscale deployments have a $1/month base fee plus usage-based compute charges.

### How do I handle database connections in Flask on Replit?

Enable the PostgreSQL database from the Tools dock. Replit injects DATABASE_URL as an environment variable. Use it with SQLAlchemy or psycopg2 to connect. The database persists across deployments.

### What Python version does Replit use?

The Python version depends on your Nix configuration. Specify the version in .replit under [nix] packages, for example pkgs.python311 for Python 3.11. Check available versions at search.nixos.org/packages.

### Can I use Django instead of Flask on Replit?

Yes. Replit supports Django with the same deployment configuration pattern. Replace Flask-specific commands with Django's: use gunicorn project.wsgi:application for the deployment run command and python manage.py migrate in the build step.

---

Source: https://www.rapidevelopers.com/replit-tutorial/how-to-deploy-a-flask-web-application-using-replit-s-built-in-hosting
© RapidDev — https://www.rapidevelopers.com/replit-tutorial/how-to-deploy-a-flask-web-application-using-replit-s-built-in-hosting
