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.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
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.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →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
- [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.
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
- Create a private project directory and enter it.
mkdir -p ~/flask-api cd ~/flask-api - Create the virtual environment. Use
python3when it is the supported interpreter, or substitutepython3.12explicitly:python3 -m venv .venv - Activate it in your shell.
. .venv/bin/activateYour prompt normally gains a
(.venv)prefix. Every package command in this guide assumes the environment is active. - 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.
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
- 【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.
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:
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
- [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.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallPlan 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:
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
- 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:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems[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.
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.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →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:
Best Value
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.
Recommended Free Tools
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 --debugonly on a development machine. - Exercise success, malformed-input and not-found cases with
curlor 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.
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.
Quick Recap
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.

