Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
FREEDOM251

Freedom251 › Blog › Flask

How to Create a REST API With Flask on Ubuntu

By Freedom251 Team · · 11 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To create a REST API with Flask on Ubuntu, install a supported Python version, isolate the project in a virtual environment, define JSON routes, test them with curl, then run the app behind Gunicorn and nginx for production. Flask 3.1.x supports Python 3.9 and newer, but Gunicorn’s current installation guidance requires Python 3.12 or newer. That difference matters: Python 3.12 is the practical baseline for an Ubuntu server that will run the complete production stack.

What you are building

Flask is a lightweight WSGI web application framework. A REST-style API is a set of HTTP endpoints with explicit paths, methods, status codes and response schemas. Flask maps each endpoint to a Python function; the function returns JSON, an HTTP status, or both.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The finished deployment has four layers:

  • Flask application: defines routes, validates input and produces responses.
  • Gunicorn: a production WSGI server that keeps worker processes running.
  • nginx: the public reverse proxy that accepts HTTP or HTTPS traffic and forwards requests to Gunicorn.
  • systemd: starts the service at boot, restarts it after failure and runs it as an unprivileged account.

Use Flask’s built-in server only while developing. It includes a debugger and is not intended to be your Internet-facing production server.

Check Python and Ubuntu prerequisites

Flask and Gunicorn version requirements

Flask 3.1.x supports Python 3.9 and newer. Gunicorn’s current installation page lists Python 3.12 or newer. Therefore, check the interpreter before creating the environment:

#1 Best Overall
Sale
TOZO A1 Wireless Earbuds Bluetooth 5.3 Light Weight in Ear Headphones | IPX5 Waterproof,2 Mics for AI Calls,Immersive Premium Sound Bass Headset with Charging Case,32 EQ via App Black
  • [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
  • [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
  • [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
  • [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
  • [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.
python3 --version
python3.12 --version

If python3 reports 3.9, 3.10 or 3.11, Flask can run, but the current Gunicorn release may not install. Install or select Python 3.12 on the Ubuntu release you administer, and use that executable explicitly when creating the virtual environment. Do not replace Ubuntu’s system Python casually; use a separately installed interpreter and leave operating-system tools on their supported version.

Install operating-system packages

On a new Ubuntu machine, install Python, virtual-environment support, pip and nginx:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo apt update
sudo apt install python3 python3-venv python3-pip nginx

For a Python 3.12 environment, your Ubuntu repositories must provide the corresponding python3.12 and python3.12-venv packages. The exact package names and availability depend on the Ubuntu release; verify them with apt policy before installing. A missing ensurepip or venv module usually means the version-specific venv package is not installed.

Create the project and virtual environment

  1. Create a private project directory and enter it.
    mkdir -p ~/flask-api
    cd ~/flask-api
  2. Create the virtual environment. Use python3 when it is the supported interpreter, or substitute python3.12 explicitly:
    python3 -m venv .venv
  3. Activate it in your shell.
    . .venv/bin/activate

    Your prompt normally gains a (.venv) prefix. Every package command in this guide assumes the environment is active.

  4. Upgrade packaging tools and install the application dependencies.
    python -m pip install --upgrade pip
    pip install Flask gunicorn

A virtual environment keeps project libraries separate from Ubuntu’s operating system and from other applications. When you open a new shell, reactivate it with . .venv/bin/activate; deactivation is simply deactivate.

Record the exact packages used by a deployment so another machine can reproduce it:

pip freeze > requirements.txt

For a controlled build, review that file before committing it. It may include transitive packages and platform-specific entries, so regenerate it whenever you intentionally change dependencies.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Write a small JSON API

Application file

Create app.py in the project directory:

from flask import Flask, jsonify, request

app = Flask(__name__)


@app.get('/api/health')
def health():
    return jsonify(status='ok')


@app.get('/api/items')
def list_items():
    return jsonify(items=[])


@app.post('/api/items')
def create_item():
    payload = request.get_json(silent=True) or {}
    return jsonify(item=payload), 201

The module-level object named app is the WSGI application. That is why the Gunicorn target later is app:app: the first app is the module filename and the second is the Flask object.

What each endpoint means

Endpoint Method Successful response Purpose
/api/health GET 200, {"status":"ok"} Basic process or load-balancer health check
/api/items GET 200, {"items":[]} Returns the collection
/api/items POST 201, {"item": ...} Accepts a JSON payload and creates an item

This example deliberately has no database, authentication or schema validation. It demonstrates routing and JSON only. A real API must define whether fields are required, how unknown fields are handled, how an item is stored, and which errors clients can rely on.

Rank #2
Sale
Smart Watch for Men Women Compatible with iPhone Samsung Android Phone 1.69 inch Full Touch Screen IP68 Waterproof Bluetooh Fitness Tracker Heart Rate/Sleep Monitor (Black)
  • 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
  • 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
  • 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
  • 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
  • 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.

Make the contract explicit

Before adding more routes, write down the contract for each one:

  • Path and HTTP method, including whether a trailing slash is accepted.
  • Request content type and the exact JSON fields, types and size limits.
  • Response shape for success and every expected failure.
  • Status codes such as 200 for a successful read, 201 for a created resource, 400 for malformed input, 401 or 403 for access control, 404 for a missing resource and 409 for a conflict.
  • Authentication requirements, pagination rules, filtering and sorting syntax.
  • Whether clients may retry a request safely, especially a POST.

Consistent schemas are more valuable than clever route code. Return an object with stable field names rather than exposing Python objects or database-specific representations.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run and test Flask locally

With the virtual environment active, start the development server:

flask --app app run --debug

By default it listens on a local address and port shown in the terminal. The debug reloader may start the process twice, and the interactive debugger can expose sensitive information. Never expose this mode to the public Internet.

In another terminal, exercise each route:

curl -i http://127.0.0.1:5000/api/health
curl -i http://127.0.0.1:5000/api/items
curl -i -X POST http://127.0.0.1:5000/api/items 
  -H 'Content-Type: application/json' 
  -d '{"name":"example"}'

The health and collection requests should return HTTP 200. The POST should return HTTP 201 and include the submitted object under item. The -i option displays headers, which helps verify status codes and content type.

Test malformed and missing input

The sample uses request.get_json(silent=True) or {}, so invalid JSON becomes an empty object. That is convenient for a demonstration but usually too permissive for a production contract. Reject malformed or missing JSON explicitly:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from flask import Flask, jsonify, request

app = Flask(__name__)


@app.post('/api/items')
def create_item():
    if not request.is_json:
        return jsonify(error='Content-Type must be application/json'), 415

    payload = request.get_json(silent=True)
    if not isinstance(payload, dict):
        return jsonify(error='Request body must be a JSON object'), 400

    name = payload.get('name')
    if not isinstance(name, str) or not name.strip():
        return jsonify(error='name is required'), 400

    return jsonify(item={'name': name.strip()}), 201

Choose validation behavior deliberately and document it. Do not silently discard invalid data merely because Flask can parse it.

Organize a larger Flask API

Separate configuration from code

Keep secrets, database URLs and environment-specific settings out of source files. Read them from environment variables or a protected configuration mechanism, and ensure they are not printed in exception pages or logs. Set a production-appropriate Flask configuration rather than relying on debug defaults.

Rank #3
New Bee Guitar Cable 10ft Electric Instrument Bass AMP Cord | 6.35mm Jack, Durable Design, Stage and Home Studio Use, Low Noise Shielded Electric Cord for Bass, Keyboard, Amp, Mixer
  • [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
  • [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
  • [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
  • [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
  • [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by

Use blueprints for route groups

When an API gains multiple resources, place related routes in blueprints such as items, users and admin. Register them from an application factory. This keeps imports manageable and makes testing a new application instance straightforward.

Centralize errors

Register handlers for common HTTP errors and return one documented JSON error format. Include a machine-readable code and a human-readable message, but do not return stack traces, SQL statements, filesystem paths or secret configuration. Log the detailed exception server-side with a request identifier.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Plan persistence and concurrency

The in-memory empty list in the example resets whenever the process restarts and is not shared between Gunicorn workers. Use a transactional database for durable data and design transactions so two simultaneous requests cannot overwrite one another unexpectedly. If work takes longer than a normal HTTP request, queue it and return a job identifier instead of blocking a worker indefinitely.

Choose a production process model

Choice Use it when Important limitation or setting
Flask development server Local coding and debugging Not a production server; debug mode can disclose sensitive data
Gunicorn synchronous workers Most ordinary Flask APIs with short requests Each worker handles one request at a time; slow I/O reduces capacity
Gunicorn with gevent workers Many long-running concurrent connections, when the application and dependencies support cooperative I/O Requires compatibility testing and a deliberate worker-class choice
Gunicorn behind nginx Public HTTP or HTTPS service nginx owns the public edge; Gunicorn can remain on loopback or a Unix socket

Gunicorn’s default synchronous worker is appropriate for most uses. Consider gevent only when the workload genuinely has numerous long-lived connections and the complete dependency stack is compatible. Worker count is workload-specific; the example below starts four workers, but you should load-test and monitor memory before changing that value.

Run Gunicorn manually first

From the project directory with the virtual environment active, bind Gunicorn to loopback:

gunicorn --workers 4 --bind 127.0.0.1:8000 app:app

Binding to 127.0.0.1 prevents direct access from other network hosts when nginx is the public edge. In another shell, test the same endpoint through port 8000:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -i http://127.0.0.1:8000/api/health

Gunicorn should not run as root because that would execute application code with root privileges. If the command fails, inspect the Python interpreter used to install Gunicorn and the one used to launch it:

which python
python --version
which gunicorn
gunicorn --version

Using the executable inside .venv/bin avoids accidentally launching a system-wide installation.

Rank #4
BSIMB 15.6" Digital Family Calendar & Chore Chart, No Subscription Fees
  • Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
  • Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
  • All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
  • Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
  • Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.

Create a systemd service

A terminal command ends when the shell closes. A systemd unit provides a durable service with a dedicated user, a working directory and restart behavior. First create an unprivileged account and place the application where that account can read it. The exact username and path are your administrative choices; the unit below uses flaskapi and /srv/flask-api.

sudo adduser --system --group --home /srv/flask-api flaskapi
sudo mkdir -p /srv/flask-api
sudo chown -R flaskapi:flaskapi /srv/flask-api

Copy the application and create its virtual environment as that account, or deploy through your normal release process. Then create /etc/systemd/system/flask-api.service:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
[Unit]
Description=Flask API served by Gunicorn
After=network.target

[Service]
User=flaskapi
Group=flaskapi
WorkingDirectory=/srv/flask-api
Environment="PATH=/srv/flask-api/.venv/bin"
ExecStart=/srv/flask-api/.venv/bin/gunicorn --workers 4 --bind 127.0.0.1:8000 app:app
Restart=on-failure

[Install]
WantedBy=multi-user.target

Load and start the unit:

sudo systemctl daemon-reload
sudo systemctl enable --now flask-api.service
sudo systemctl status flask-api.service
curl -i http://127.0.0.1:8000/api/health

For diagnostics, inspect recent logs:

sudo journalctl -u flask-api.service -n 100 --no-pager
sudo journalctl -u flask-api.service -f

If the unit fails, check the path in WorkingDirectory, execute the exact .venv/bin/gunicorn command manually as flaskapi, and confirm that the account can read every imported file and write only to directories that truly need writes.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use a Unix socket with nginx

A TCP loopback bind is simple. A Unix socket is another well-supported shape: systemd or Gunicorn creates a local socket, and nginx proxies to it. The socket must have permissions that allow nginx to connect.

Socket-based systemd unit

Create /etc/systemd/system/flask-api.socket:

[Unit]
Description=Flask API socket

[Socket]
ListenStream=/run/flask-api.sock
SocketUser=flaskapi
SocketGroup=www-data
SocketMode=0660

[Install]
WantedBy=sockets.target

Change the service’s ExecStart to use the socket and remove the TCP bind:

ExecStart=/srv/flask-api/.venv/bin/gunicorn --workers 4 --bind unix:/run/flask-api.sock app:app

Enable the service and socket:

sudo systemctl daemon-reload
sudo systemctl enable --now flask-api.socket
sudo systemctl enable flask-api.service
sudo systemctl status flask-api.socket

Socket activation and a directly managed service are alternative designs. Do not enable competing units that try to own the same listener. If nginx receives a gateway error, inspect whether the socket exists and whether its group and mode permit nginx access.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure nginx as the public proxy

Create a server block such as /etc/nginx/sites-available/flask-api:

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://unix:/run/flask-api.sock;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Replace api.example.com with a hostname you control. Enable and validate the site:

sudo ln -s /etc/nginx/sites-available/flask-api /etc/nginx/sites-enabled/flask-api
sudo nginx -t
sudo systemctl reload nginx

Then call the public hostname:

curl -i http://api.example.com/api/health

Keep nginx in front when serving ports 80 or 443. It can handle client connections and TLS while Gunicorn concentrates on the application. Configure HTTPS, certificate renewal, request-size limits and access-log retention according to your site’s operational requirements; do not assume the example HTTP block provides encryption.

Secure and operate the deployed API

Authentication and authorization

HTTPS protects credentials in transit, but it does not decide who may call an endpoint. Choose an authentication method appropriate to your clients, validate tokens or sessions on every protected route, and enforce authorization at the resource level. Never put long-lived secrets in query strings or commit them to the repository.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Input, output and resource limits

  • Validate JSON types, required fields, string lengths and numeric ranges.
  • Set a maximum request body size in nginx and Flask appropriate to the API.
  • Paginate collections rather than returning unbounded result sets.
  • Use parameterized database queries and escape output for any downstream context.
  • Rate-limit expensive or unauthenticated operations at the edge or application layer.

Logging and health checks

Log method, path, status, duration and a request identifier, while excluding authorization headers, passwords and sensitive payloads. Monitor process restarts, worker timeouts, memory use, response latency and 4xx/5xx rates. Keep /api/health inexpensive and free of dependencies that would make a basic process check fail unnecessarily; use a separate readiness check when you need to verify database or queue availability.

Updates and rollback

Deploy into a versioned release directory or otherwise keep the previous working application available. Install dependencies from a reviewed requirements file, run tests before switching the service, and use systemctl restart or a controlled reload only after the new release is ready. A rollback should restore both code and compatible database migrations.

Troubleshoot common failures

Symptom Likely cause Checks and fix
ModuleNotFoundError: flask Virtual environment is not active or Flask was installed elsewhere Activate .venv; run python -m pip show Flask; use the environment’s interpreter
Gunicorn will not install Python is below the current Gunicorn requirement Check python --version; use Python 3.12 or newer for the deployment environment
Gunicorn cannot import app:app Wrong working directory, filename or object name Run from the directory containing app.py; verify the module and Flask object names
nginx returns 502 Bad Gateway Gunicorn is stopped, listening elsewhere or the socket is inaccessible Check systemctl status, journalctl, socket existence and nginx’s account permissions
POST returns an unexpected empty item The demonstration endpoint accepts invalid or absent JSON Send Content-Type: application/json; add explicit validation and a documented error response
Changes do not appear Old worker processes are still serving the release Restart or reload the systemd service after deploying, then verify the running unit’s working directory

Development versus production checklist

Before local development

  • Confirm the Python version and create a project-specific .venv.
  • Install Flask inside that environment.
  • Run flask --app app run --debug only on a development machine.
  • Exercise success, malformed-input and not-found cases with curl or automated tests.

Before going public

  • Use Python 3.12 or newer if required by the Gunicorn version you install.
  • Install Gunicorn in the same virtual environment as Flask.
  • Run Gunicorn as a dedicated unprivileged user, never root.
  • Supervise it with systemd and choose either a loopback TCP listener or a Unix socket.
  • Put nginx in front, validate its configuration with nginx -t, and configure HTTPS before handling credentials.
  • Document schemas, status codes, authentication, limits and error formats.
  • Configure logs, monitoring, backups and a tested rollback procedure.

FAQ

Can Flask’s built-in server handle production traffic?

No. It is intended for local development and debugging. Use Gunicorn or another production WSGI server, normally behind nginx.

Why does Flask support an older Python version than Gunicorn?

They are separate packages with separate support policies. Flask 3.1.x supports Python 3.9 and newer, while current Gunicorn installation guidance requires Python 3.12 or newer. Select an interpreter that satisfies every production dependency.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I bind Gunicorn to 0.0.0.0?

Not when nginx is the public edge. Bind to loopback or use a Unix socket so clients cannot bypass nginx’s controls.

Do I need a database for this example?

No, the sample demonstrates routing only. Its empty collection is not durable and is not shared safely between workers. Add a transactional persistence layer before treating it as a real data service.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

FlaskGunicornNGINXPythonREST APIsystemdUbuntu

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.