The dependable production pattern is nginx → Gunicorn → Flask → PostgreSQL on a Linux VPS. Create a non-root deployment account, run the Flask WSGI application under Gunicorn supervised by systemd, keep PostgreSQL behind a dedicated role and controlled authentication, and let nginx terminate HTTPS and proxy requests internally. This guide shows the complete setup, a safer release process, and the checks that catch the common 502, database, static-file, and redirect failures.
Antihost offers Linux VPS plans with full root access, unlimited traffic on a 1 Gbps port and a 14-day free trial.
Choose the deployment shape before you provision a server
A VPS can host both the application and PostgreSQL, or the application can use a managed PostgreSQL service. The web tier is the same in either case: nginx accepts public traffic, Gunicorn serves the WSGI app on a private address or Unix socket, and Flask handles requests. The database choice changes who patches, monitors, secures, and restores PostgreSQL.
As an Amazon Associate I earn from qualifying purchases.
Free tools Windows power users keep installed
One-click scans. No signup required.
| Option | Operational control | Setup effort | Security boundary | Scaling path | Database administration | Backups | Monthly cost |
|---|---|---|---|---|---|---|---|
| PostgreSQL on the same VPS | Full control of the operating system, PostgreSQL version, extensions, and configuration | Lower initial complexity because the database is local | One host contains web and database workloads; a compromise or outage affects both | Vertical VPS upgrades first, then move PostgreSQL to a separate host or managed service | You handle updates, roles, authentication, monitoring, and disk capacity | You schedule, store, and test your own logical or physical backups | Usually no separate database fee, but the VPS must have enough CPU, memory, and storage |
| Managed PostgreSQL | Less control over host-level settings, but provider tooling for versions and maintenance | Faster database provisioning; requires network, TLS, and allow-list configuration | Database is separated from the web host and normally has provider isolation controls | Service tiers, replicas, and storage can usually be changed independently of the VPS | Provider handles much of the platform maintenance; you still manage schemas, roles, and application queries | Provider options reduce administration, but you must verify retention and perform your own restore checks | Recurring service cost in addition to the VPS and any network or storage charges |
For a small application with modest traffic, a local database is straightforward if you are prepared to own patching and recovery. Choose managed PostgreSQL when database availability, independent scaling, or reduced operations work matters more than the extra recurring cost. Do not expose a local PostgreSQL listener to the public internet merely to make a future migration easier.
What you need before starting
- A supported Ubuntu or other Linux VPS with a public IP address and a DNS name you control.
- SSH access using a non-root account with administrative privileges. Root should be reserved for provisioning and host administration.
- A Flask project with a production WSGI entry point, such as
wsgi:app, or a documented application-factory target. - A dependency lock or pinned
requirements.txt, including your PostgreSQL driver and migration package. - A plan for the database name, application role, secret storage, backups, and a health endpoint.
- DNS pointing the intended hostname at the VPS before requesting an HTTPS certificate.
Flask’s documentation explicitly says not to use its development server in production; it is intended only for local development. Gunicorn’s documentation likewise warns that it must not run as root because the application would then run with root privileges.
#1 Best Overall
- BLUE LIGHT BLOCKING GLASSES: Our blue light glasses for women boast UV400 protection, blocking harmful blue rays from computer screens. With these bluelight glasses women, you can enjoy gaming, reading, or watching TV with no worries of eye strain.
- EYE RELIEF: Our blue light glasses are engineered to reduce blurry vision or strain caused by prolonged screen time. These womens blue light glasses aid in better sleep by preventing blue light from disrupting your natural sleep cycle, helping you fall asleep faster and enjoy a more restful night's sleep, making them the perfect christmas present.
- COMFORTABLE: Crafted from durable material, our trendy blue light glasses for woman or blue light glasses men ensure long-lasting comfort. The high-transmittance lenses accessories, gamer gifts for men, help keep your eyes relaxed. Perfect stocking stuffers, gifts for gamers men, christmas gifts for men, halloween costumes.
- LIGHTWEIGHT: These clear blue light glasses feature a lightweight frame, meaning they’ll stay comfy for long-term wear. Perfect as blue blockers glasses for women or mens blue light glasses, these non-prescription fake glasses for men can be worn throughout the day. The blue light blocking glasses women feature polycarbonate HD lenses, which are anti-reflective and restore true color.
- WORRY-FREE PURCHASE: If you experience any issues, our support team is ready to assist you quickly. These are the perfect costume glasses for cosplay dressing up.
Prepare Ubuntu and create an application account
Connect and update the host
Log in with the administrative SSH account, apply security updates, and install the runtime, web server, source-control client, and PostgreSQL packages. PostgreSQL’s Ubuntu instructions use the distribution package as the baseline:
sudo apt update
sudo apt full-upgrade -y
sudo apt install -y python3 python3-venv python3-pip git nginx postgresql
If your distribution separates the PostgreSQL client or development headers needed by your Python driver, install those packages before building dependencies. Keep the operating system’s package updates part of your regular maintenance schedule.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Create a non-root service user and directory
sudo adduser --system --group --home /srv/flaskapp flaskapp
sudo mkdir -p /srv/flaskapp
sudo chown -R flaskapp:flaskapp /srv/flaskapp
Switch to that account for application files. A service account without a login shell or broad administrative rights limits the damage from an application-level compromise.
Install the application in an isolated virtual environment
sudo -u flaskapp -H bash
cd /srv/flaskapp
git clone YOUR_REPOSITORY_URL current
cd current
python3 -m venv /srv/flaskapp/.venv
. /srv/flaskapp/.venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
exit
Replace YOUR_REPOSITORY_URL with your repository address; do not put credentials in a clone URL that can be recorded in shell history. Pin application and driver versions so a release does not silently receive a different dependency tree.
Install and lock down PostgreSQL
Create a dedicated role and database
Do not let web requests connect as PostgreSQL’s superuser. Create a role that owns only the application database and grant the minimum privileges your migration and runtime model require.
sudo -u postgres psql
CREATE ROLE app_user LOGIN PASSWORD 'generate-a-long-unique-password';
CREATE DATABASE app_db OWNER app_user;
c app_db
REVOKE ALL ON SCHEMA public FROM PUBLIC;
GRANT USAGE, CREATE ON SCHEMA public TO app_user;
q
Use a generated secret rather than the example text. If your migration process needs to create extensions or alter objects outside the application schema, grant those permissions deliberately for the migration step instead of making the runtime role a superuser.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #2
- WINDEX GAMING WIPES: effortlessly clean all your gaming gear and gaming electronics in one easy step with our new Windex Gaming Wipes
- AMMONIA FREE: our ammonia-free formula is safe to use on all of your favorite gaming equipment like consoles, headsets, controllers, screens, keyboards, and more
- PRE-MOISTENED WIPES: each wipe instantly removes dust, dirt, and stubborn smudges and fingerprints leaving a streak-free shine
- ANTI-STATIC: great for cleaning and wiping down your gaming gear and electronics leaving behind no visible residue
- COMPACT RESEALABLE PACKAGE: store your pack of Windex Gaming Wipes anywhere in your home for whenever a quick and easy wipedown is needed
Understand local and network authentication
Ubuntu’s PostgreSQL configuration resides under /etc/postgresql/<version>/main. Local Unix-socket connections commonly use peer authentication, which maps the operating-system account to a database role. TCP connections, including 127.0.0.1, are controlled by host rules and can use scram-sha-256. The file pg_hba.conf determines which clients may authenticate and by what method.
For an application service running as flaskapp, a password-authenticated loopback connection is usually less surprising than peer authentication. Inspect the existing rules before editing them, add the narrowest rule that matches the application database and role, then reload PostgreSQL:
sudo systemctl reload postgresql
sudo -u postgres psql -c "SHOW hba_file;"
ss -ltnp | grep 5432
Keep PostgreSQL bound to localhost when it shares the VPS. If a separate application host must connect, bind only to the required private interface, restrict the source addresses in both the firewall and pg_hba.conf, and require TLS. Ubuntu notes that its bundled SSL setup is suitable for testing; production connections should use a certificate issued by a trusted certificate authority.
Test the credentials before involving Flask
PGPASSWORD='your-generated-password' psql
--host=127.0.0.1 --port=5432
--username=app_user --dbname=app_db
-c 'select current_user, current_database();'
Do not leave a password in shell history on a shared system. Prefer the protected environment file described below for service tests.
Configure Flask without putting secrets in Git
Create the protected environment file
Store the database URL, Flask configuration, and secret key outside the repository. A systemd environment file is a simple approach for one VPS:
sudo install -o root -g flaskapp -m 0640 /dev/null /etc/flaskapp.env
sudoedit /etc/flaskapp.env
FLASK_CONFIG=production
FLASK_DEBUG=0
SECRET_KEY=replace-with-a-random-value
DATABASE_URL=postgresql://app_user:[email protected]:5432/app_db
URL-encode reserved characters in the password. Keep the file readable only by root and the service group, and ensure application logs never print the full connection string or secret key. If a secret is committed or appears in logs, revoke and rotate it rather than merely deleting the visible line.
Rank #3
- Ergonomic & Adjustable Design: Fight "text neck" and improve your posture. Designed for comfort, it offers an adjustable height (7.1in-8.5in) (1.4in adjustable range) and angle (5°-85°) Please note: cell phone stand is only 9 inches tall—the length of a standard pencil
- Without blocking subtitles Perfect for office desk Tiktok Face Time Zoom video—this is the ultimate kitchen desk essential for phones! Perfect viewing experience while reducing neck back strain, A must-have for gamers streamers desk, and anyone who values both style and support!
- Sleek & Stable Design fits Otterbox Case: Say goodbye to bent cables, tangles, and tip-overs! LISEN’s stylish adjustable desk phone holder features a built-in charging hole and smart cable management, keeping your phone secure and your workspace clutter-free cool gadgets for men
- ANTI-SLIP DESIGN free hands: The under 10.00 items easy gadgets back and the bottom of this kitchen essentials for women are fully covered by anti-skid silicone, which LISEN stand up desk decor can provide maximum protection device from any scratches and slides
- Your Perfect Viewing Companion:If your product arrives damaged or breaks within 365 days, reach out to LISEN for a quick and hassle-free solution. Struggling with assembly, like tightening the screw? We’ve got your back! Your experience matters to us. (Product including: phone Holder stand*1, screw*1, hexagon screwdriver*1, specification*1)
Expose a stable WSGI target
A minimal wsgi.py can expose an imported application:
from myapp import create_app
app = create_app()
With that layout, Gunicorn targets wsgi:app. If your project exports an already-created application, use its actual module and variable. For a factory, Gunicorn can target the factory form supported by your project; test the exact target from the virtual environment before creating the service.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Apply migrations deliberately
Use the project’s migration tool, such as Flask-Migrate/Alembic, to apply reviewed schema changes. Do not generate an unreviewed migration on the production host. Run the migration command with the same environment file and virtual environment that the service will use, and record the revision in your release notes.
Run Flask with Gunicorn under systemd
Create the service unit
Save this unit as /etc/systemd/system/flaskapp.service. The example binds only to loopback; nginx will be the public-facing process.
[Unit]
Description=Gunicorn service for Flask application
After=network.target
[Service]
User=flaskapp
Group=flaskapp
WorkingDirectory=/srv/flaskapp/current
EnvironmentFile=/etc/flaskapp.env
Environment="PATH=/srv/flaskapp/.venv/bin"
ExecStart=/srv/flaskapp/.venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app
Restart=always
[Install]
WantedBy=multi-user.target
Change WorkingDirectory and the WSGI target to match the checkout. Three workers is only an example, not a universal performance rule. Worker memory, request time, concurrency, and available CPU determine the appropriate value; begin conservatively and observe memory pressure and latency.
Enable, inspect, and test it
sudo systemctl daemon-reload
sudo systemctl enable --now flaskapp
sudo systemctl status flaskapp
sudo journalctl -u flaskapp -n 100 --no-pager
curl --fail http://127.0.0.1:8000/health
Do not continue to nginx until the local request succeeds and the journal shows no import, configuration, or database errors. Gunicorn can also be activated through a systemd socket and proxied over a Unix socket; that removes the TCP port from the internal path but adds socket ownership and permission details. Whichever form you choose, nginx and Gunicorn must use exactly the same endpoint.
Rank #4
- Easy Installation: Our gaming lights have been designed to enhance your entertainment experience. Simply position the led light bar next to your TV or game console to create a game room environment with ease. They come with USB power and a 60 inch cord for direct mounting and mobility
- Audio Syncing & Extensive Mode: The built-in high-sensitivity music sensor allows you to sync the TV led lights to your favorite music. Lights react and move to the beat of music, film and game audio, creating an incredible immersive sound and light experience – and all without the need for an additional hub. You can choose from 64+ patterns, including strobe, illusion, jump, energy, gradient, running, chasing, fluttering mode etc. Each mode can be applied to different scenes
- 64+ Chasing IC+RGB & W Effect: The independent control design of IC+RGB and white all-in-one multifunctional rgbw led bar lights monitor backlight allows you to easily switch the led lights for desk between IC+RGB and white to enjoy a variety of lighting effects, you can choose white lighting mode with high brightness for better illumination when working, etc. Choose IC+RGB lighting mode when you need to create an entertaining atmosphere for the light show, 16 million colours of Color changing rgb light bar let you dance to the beat and create a dynamic party atmosphere
- Dynamic and Dimmable Lights: 1%-full brightness free brightness adjustment gives you the flexibility to adjust dimmable led light bar for room decorations or a variety of occasions
- USB Powered: USB light bars for room can be powered by powernank, usb charger, TV, Monitor, laptop PC mainframe, making behind TV led lighting bar easy to install in any location.TV led lights are powered by the TV usb interface / PC mainframe, can be turned on/off synchronously with TV / PC
Put nginx in front of Gunicorn
Configure the HTTP reverse proxy
Create a site configuration such as /etc/nginx/sites-available/flaskapp. This example serves static files directly and forwards dynamic requests to the loopback Gunicorn listener.
server {
listen 80;
server_name app.example.com;
location /static/ {
alias /srv/flaskapp/current/static/;
expires 7d;
add_header Cache-Control "public";
}
location / {
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;
proxy_pass http://127.0.0.1:8000;
}
}
sudo ln -s /etc/nginx/sites-available/flaskapp /etc/nginx/sites-enabled/flaskapp
sudo nginx -t
sudo systemctl reload nginx
The proxy_pass address must exactly match Gunicorn’s --bind value. The nginx worker needs read and directory-traverse permission for the static path; never make the entire application tree world-writable.
Enable HTTPS and redirect HTTP
Use an established ACME client such as Certbot after DNS resolves to the VPS. Request the certificate for the real hostname, enable automatic renewal, and verify it with a dry run. The resulting nginx configuration should listen on 443 with the certificate and key, retain the proxy headers, and redirect port 80 to HTTPS.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorssudo certbot --nginx -d app.example.com
sudo certbot renew --dry-run
Do not claim HTTPS is complete until an external request receives a valid certificate, port 80 redirects to the canonical HTTPS URL, and renewal testing succeeds. Configure Flask’s proxy handling so it trusts the known proxy and treats the original request as HTTPS; otherwise generated URLs may use http and secure-cookie behavior may be incorrect. Trusting forwarded headers from arbitrary clients is unsafe, so only nginx should be able to reach Gunicorn.
Open only the ports the service needs
Configure the host firewall to allow SSH and the public web ports, while leaving PostgreSQL private:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
If SSH uses a nonstandard port, allow that port before enabling the firewall. Do not add a public rule for 5432 when the database is local. For a managed database, permit only its documented private address or endpoint requirements and use TLS; never open PostgreSQL to every source address.
Best Value
- Compatible with Wide Screens - To ensure compatibility with the dual monitor mount, your each monitor must meet three conditions at the same time: First, computer screens size range: 13 to 32 inches. Second, screen weight range: 4.4 to 19.8 lbs. Third, the back of the monitor screen must have VESA mounting holes with a pitch of 75x75mm or 100x100mm.
- Regarding the compatibility with desks - Your desk must meet three conditions at the same time: First, desk material: Only wooden desks are recommended, plastic or glass desks cannot be used. Second, desk thickness range: 0.59" - 3.54". Third, the bottom of the desk should not have any cross beams or panels, as this will interfere with installation. We recommend carefully checking that your desk and monitors meets all above conditions before purchasing.
- Dual C-Clamp Hold - Worried your dual monitors might wobble or slip? Our upgraded base uses a larger platform plus a dual C-clamp structure to lock the dual monitor arm firmly to your desk. Each arm safely keeps your screens steady while you type, click and game—no shaking, no sliding, just a clean and secure setup you can trust every day. It also provides Grommet Mounting installation choice, both options ensure stable and secure fixation for your 0.59" - 3.54" desk.
- Full-Motion Adjustment For Comfortable View - Pull the screen closer when you’re deep in a spreadsheet, push it back to watch videos, or rotate to portrait for coding — moving everything smoothly with just one hand. The monitor stand offers +85°/-50° tilt, ±90° swivel and 360° rotation. Raise your monitor up to 15.75″ to support a healthy sitting posture. Whether you’re working from home, gaming through the night, or switching between video calls and documents, getting the screens to your natural line of sight helps relieve neck, shoulder and back strain so you can stay focused longer with less fatigue.
- Keep Your Desk Organized: By lifting both screens off the desktop, this dual monitor stand opens up valuable space for your keyboard, notebook, docking station or a simple, clutter-free work area. Built-in cable management guides wires along the arms, keeping cords out of sight and out of the way. Enjoy a tidy, modern workstation that looks as good as it feels to use.
Use a repeatable, reversible release process
- Fetch a known revision. Keep the current checkout available so a rollback does not depend on an unavailable repository or an unpinned dependency.
- Install locked dependencies. Run the virtual environment’s package installer and fail the release if dependency resolution fails.
- Check configuration. Confirm debug is off, the environment file is readable by the service, and the database URL points to the intended database.
- Apply reviewed migrations. Run the migration tool once, record the resulting revision, and stop if it reports an error.
- Reload Gunicorn gracefully. Use
sudo systemctl restart flaskappor a controlled reload appropriate to your deployment so existing requests are not abandoned unnecessarily. - Validate the service. Check
systemctl status flaskapp, inspect recent journal entries, test the health endpoint locally, runsudo nginx -t, and request the public HTTPS URL. - Retain rollback data. Keep the previous revision and know which migration reversal or database restore procedure is safe; code rollback alone cannot undo an incompatible schema change.
A release is not successful merely because a command returned without an obvious error. Verify process status, nginx syntax, application logs, database connectivity, and a real HTTPS request every time.
Back up PostgreSQL and prove that recovery works
PostgreSQL databases should be backed up regularly. Choose logical dumps, physical backups, or both according to database size and recovery objectives, store copies outside the VPS, protect them as sensitive data, and monitor that scheduled jobs actually complete. A backup that has never been restored is an assumption, not a recovery plan.
- Define how much data loss the application can tolerate and how quickly it must return.
- Keep backup credentials separate from the runtime role where practical.
- Test a restore on an isolated instance, then start the application against the restored database and run representative health checks.
- Document retention, encryption, storage location, and the person responsible for responding to a failed backup.
Troubleshoot by following the request path
ModuleNotFoundError or Gunicorn import failure
- Confirm the package is installed in
/srv/flaskapp/.venv, not only in a different user’s Python environment. - Check
WorkingDirectoryand the exactmodule:apptarget inExecStart. - Run the target manually as
flaskappwith the same environment file, then inspectjournalctl -u flaskapp.
nginx returns 502 Bad Gateway
- Run
systemctl status flaskappand confirm Gunicorn is listening on127.0.0.1:8000or the selected Unix socket. - Compare nginx’s upstream with Gunicorn’s bind address character for character.
- For a socket deployment, check that nginx can traverse the directory and read/write the socket under its configured user and group.
Database authentication fails
- Check the spelling, URL encoding, role, database, and host in
DATABASE_URL. - Inspect the matching
pg_hba.confrule and whether PostgreSQL is listening on the address used by the URL. - Test with
psqlusing the same host, port, role, and database; then reload PostgreSQL after authentication changes.
Static files are missing or return 403
- Verify the nginx
aliaspath ends at the correct directory and matches the URL prefix. - Ensure nginx can read the files and traverse every parent directory.
- Keep large, immutable assets out of Flask’s request path so application workers remain available for dynamic requests.
HTTPS redirect loops or insecure generated URLs
- Confirm nginx sends
X-Forwarded-Protoand that Flask is configured to trust the known proxy. - Check whether an upstream load balancer is adding or rewriting forwarding headers.
- Inspect secure-cookie and canonical-URL settings after testing both the HTTP redirect and a direct HTTPS request.
Production readiness checklist
- Development server disabled; Gunicorn runs as
flaskapp, never root. - nginx is the only public application entry point, with HTTPS enabled and renewal tested.
- PostgreSQL uses a dedicated role and database; the superuser is not in
DATABASE_URL. - Port 5432 is private unless a deliberately restricted private network is required.
- Debug is off, secrets are outside Git, and the environment file permissions are restricted.
- Migrations are reviewed and applied as an explicit release step.
- Backups run on schedule, are stored separately, and have passed a restore drill.
- Health checks, systemd logs, nginx logs, disk space, memory, and database capacity are monitored.
- A previous code revision and a tested rollback decision are available before each release.
Frequently Asked Questions
How should a Flask application factory be targeted by Gunicorn?
Expose the factory from a stable module and use the factory target supported by your project, then run it manually from the virtual environment before placing that target in the systemd unit. An application object exported as wsgi:app is the simplest arrangement.
Is a Unix socket better than 127.0.0.1 for nginx-to-Gunicorn traffic?
Both keep Gunicorn private. A Unix socket removes an internal TCP port, while loopback TCP is often simpler to inspect and troubleshoot. Choose one endpoint and make nginx, systemd permissions, and Gunicorn agree exactly.
What proves that a PostgreSQL backup is usable?
Restore it to an isolated PostgreSQL instance, start the application against that copy, and run health checks and representative reads and writes. Scheduled creation alone does not verify recoverability.
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.




