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

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Deploy a Flask application on a VPS by packaging it in a Docker image, running it with Gunicorn, placing nginx on ports 80 and 443, and using DNS plus Certbot for HTTPS. Keep Gunicorn and Flask off the public network, store configuration and secrets outside the image, add health checks, and operate the stack with Docker Compose. Flask’s development server is only for local development; Flask documentation warns that it is not designed to be secure, stable, or efficient in production.

Choose the production architecture first

A practical single-VPS layout is:

Client → DNS → nginx on the VPS → Docker Compose network or loopback port → Gunicorn → Flask

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

nginx is the public front door. It terminates TLS, serves or forwards HTTP requests, and passes trusted proxy headers. Gunicorn is the production WSGI server that imports your Flask application. The Flask container is not assigned a public Internet port.

  • Publicly reachable: SSH for administration, plus TCP ports 80 and 443 for nginx.
  • Private: Gunicorn on the Compose network or a host port bound only to 127.0.0.1.
  • Persistent: a named volume or an external database for data that must survive container replacement.
  • Managed as code: a Dockerfile, a production Compose file, nginx configuration, and a documented release procedure.

You can run nginx as a host package and proxy to a loopback-published container port, or run nginx as another Compose service on the same internal network. The steps below use host nginx with a loopback binding because it makes the public/private boundary explicit. If nginx itself is containerized, proxy to the Compose service name and remove the host port publication.

#1 Best Overall
Sale
Compressed Air Duster,130000RPM Super Power Cordless Air Duster,Rechargeable Brushless Blower,3 Gear Adjustable Blower with LED Light,Dust Remover for Computer/House/Outdoor/Car
  • Powerful Turbo Fan:CAGIWIRU Electric Air Duster have reached speed up to 130000RPM,Can efficient remove dust and debris.With LED light and Three Adjustable Speed to meet with different cleaning tasks.
  • With Attached USB C Fasting Charging cable.Charing 3 hours,Enjoy up to 240 minutes of use on the lowest setting, For third speed can use for 25mins.with four charging options to meet with your needs.
  • Wide Application: attached 5 different nozzles,making it suitable for a variety of scenes, such as car,pet, pc, keyboards, and other electronic devices. It also serves for office and home clean duster.
  • Handy Design & Portable:Cordless air duster electric nozzle is Light weight and small size, design and comfortable to hold and space saving, convenient to carry around. You can put it in your room or inside the car without taking up space.
  • Economical &Reusable: Built durable materials with a long-lasting battery,our CAGIWIRU air duster it's a sustainable alternative to disposable air cans.

Prerequisites and decisions

  • An Ubuntu VPS with a static public IPv4 address, and IPv6 if you intend to publish an AAAA record.
  • A non-root administrative account that can use sudo, with SSH key authentication preferred.
  • A registered domain name whose DNS you can edit.
  • A Flask project with an importable module or application factory, a pinned requirements.txt, and a route suitable for a health check such as /health.
  • A decision about persistence: a database volume on this VPS or a managed database reached over a private or restricted connection.
  • A plan for secrets such as the Flask signing key, database password, and third-party API keys. These must not be committed to Git or copied into a Dockerfile layer.

Choose and maintain the Python base-image tag and dependency versions for your project. The python:3.12-slim image shown below is an illustrative pattern, not a claim that a particular application or dependency set was tested with it.

Prepare and secure the VPS

Create the administrator and patch the system

Log in over SSH using the non-root account, then apply available updates:

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.
sudo apt-get update
sudo apt-get upgrade -y

Keep root login and password authentication disabled after confirming that your administrative SSH key works. Record the VPS address, the SSH port, and the account used for recovery before changing SSH policy.

Install Docker Engine and Compose

Install Docker Engine and the Compose plugin from Docker’s official Ubuntu repository rather than an unrelated package source. The exact repository commands vary with the Ubuntu release, so follow the current Ubuntu installation sequence for that release. Verify both components and the daemon:

docker --version
docker compose version
sudo docker run hello-world

If you add the administrator to the docker group to run commands without sudo, treat membership as root-equivalent access. Sign out and back in before testing the new group membership.

Review firewall behavior before publishing ports

Docker-published ports can bypass assumptions made from a basic ufw or firewalld ruleset. Before starting the application, decide exactly which host ports should be reachable and review Docker’s firewall interaction for your distribution.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Allow SSH only from the networks that need it, where practical.
  • Allow TCP 80 and 443 for nginx and certificate validation.
  • Do not publish the Flask or database port on the VPS public interface.
  • After deployment, inspect listening sockets and test from an external network, not only from the VPS itself.

Make the Flask application container-ready

Use an importable application entry point

Gunicorn must be able to import your Flask application without the development reloader. An application factory is a good fit for production configuration:

from flask import Flask

def create_app():
    app = Flask(__name__)
    app.config.from_prefixed_env()

    @app.get("/health")
    def health():
        return {"status": "ok"}

    return app

With this module saved as app.py, the Gunicorn target is app:create_app(). If your module or factory has another name, change the target accordingly. The container must listen on its container interface, not only on 127.0.0.1, so Gunicorn should bind to 0.0.0.0:8000.

Rank #2
EVEO® Screen Cleaner Spray Kit (Streak-Free) TV Screen Cleaner for Smart TV, Lcd, LEd, Oled | Computer Screen Cleaner Spray and Wipe Microfiber Cloth - TV, Monitor, Laptop, iPad, Car - 6.7oz 1 Pack
  • Screen Cleaner is Your Screen’s New Bestfriend! - EVEO is proud to present a screen cleaner that’s perfectly suited for your TV. Meet your new screen cleaner, computer screen cleaner, laptop cleaner, iPad cleaner, Macbook,tv cleaner or any other electronic device. Our screen cleaner spray and wipe along with the included plush microfiber cloth, easily removes dust, fingerprints & other smudges on the screen’s surface. Clean effectively for a streak-free screen
  • Gentle on your Screens - Our screen cleaner spray and wipes is compatible to be used even for the most sensitive LCD, LED, CRT, and OLED screens in proper use. Be at ease knowing that this product is compatible with all the major brands: LG, SONY, Samsung, Sharp, iPad, iPhone, Android screens, Kindle, PC monitors - we’ve got you covered! Can be used as LCD screen cleaner, tv cleaner, smart tv screen cleaner, and more
  • Premium Super Soft Plush Microfiber Cloth - Included in this efficient screen cleaning kit. This screen cleaner spray and wipe has our signature plush microfiber cloth specially designed to comply with screens and monitors and leave them streak-free. Has been shown to effectively absorbs stubborn stains, zero streaks, and no fiber shedding. Always use this together with our screen cleaner for the best results
  • Easy-to-Use Screen Cleaner Kit + microfiber cleaning cloth - The EVEO Screen Cleaning Kit will easily be your new favorite tool when it comes to taking care of the things you love most, quick and easy solution for a streak-free screen. Simply screw on the sprayer gun on the screen cleaner bottle. Spray the cleaning liquid onto the microfiber cloth, then wipe the screen until desired cleanliness & shine is achieved.
  • Quality Screen Care Kit Worth Purchasing - Enjoy a convenient Screen Cleaner anytime, anywhere you need it. Your satisfaction is our top priority. We stand behind the quality of our products and that’s why, you shouldn’t be worrying at all. We manufacture the Best Screen Cleaner. and can be used for laptop cleaning kit, laptop screen cleaner, tv screen cleaner for smart tv. MacBook screen cleaner, monitor cleaner or screen cleaner spray for electronic devices with microfiber cleaning cloth

Pin and install dependencies

Commit a requirements.txt with versions that your project supports, including Flask and Gunicorn. Build failures should be visible during image creation rather than discovered when a container starts. Do not install development-only reloaders or depend on a workstation virtual environment being present inside the image.

Build the image

This Dockerfile installs curl so the Compose health check can make an HTTP request. If you use a different health-check command, remove that package and keep the image minimal.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
FROM python:3.12-slim

WORKDIR /app

RUN apt-get update 
    && apt-get install -y --no-install-recommends curl 
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:create_app()"]

EXPOSE documents the container port; it does not make that port public by itself. Gunicorn should not run as root. For a hardened image, create an unprivileged user, grant it ownership of /app, and add a USER instruction after dependency installation. Confirm that your application does not need to write to a root-owned directory.

Test the image before using the VPS

Build and run it locally with a temporary environment file, then request the health endpoint. The exact environment variables depend on your application, but the sequence should prove that the factory imports and Gunicorn serves traffic:

docker build -t flask-app:test .
docker run --rm -p 8000:8000 --env-file .env.example flask-app:test
curl http://127.0.0.1:8000/health

Stop the development container after the test. The production Compose file should build the same image without mounting your source tree over /app.

Define a production Docker Compose file

Web service with a private host binding

Create a production-specific Compose file, such as compose.production.yml. This example binds port 8000 only to the VPS loopback interface, removes development source-code mounts, loads production configuration, restarts the service after failures, and checks the application:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
services:
  web:
    build: .
    env_file:
      - .env.production
    ports:
      - "127.0.0.1:8000:8000"
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3

Using 127.0.0.1:8000:8000 means host nginx can reach Gunicorn while a remote client cannot connect directly to port 8000. If nginx is another Compose service, use expose: ["8000"] instead, attach both services to an internal network, and set nginx’s upstream to web:8000.

Docker’s production Compose guidance specifically calls out removing application-code volume bindings, changing ports and environment variables, and adding a restart policy such as restart: always. Do not copy a development file unchanged: bind mounts can replace the image’s code, expose debugging behavior, and make a release depend on files left on the server.

Rank #3
Sale
BladeHawks RGB Gaming Mouse Pad Extra Large Extended 31.5x12 Inch-Black | LED mouse pad with 14 lighting modes, covers keyboard & mouse, anti-slip rubber base, waterproof surface, USB powered
  • 【Large size】:Measuring 800 x 300 mm/ 31. 5×12 inches, which is wider and taller than others. Large mouse pad can perfectly fit your keyboard and mouse of any size, and there is also ample space for peripherals such as phones, tablets, etc.
  • 【LED backlight mouse pad】:Blade Hawks RGB mouse pad provides 7 static modes and 7 dynamic modes will give you the options you are looking for. Power-off memory function remembers the last lighting mode you selected. The super glow fiber Chroma will give you a colorful gaming setup that you want.
  • 【Anti-slip rubber base】:Made from Natural rubber, this gaming mousepad will firmly grip your desktop, allowing you to enjoy the ultimate precision in the games you are most passionate about. It also comes with a texture that enhances this ability even further.
  • 【Smooth and waterproof surface】: Micro-textured cloth surface, for extremely precise mouse control and a smooth movement. When liquid splashes on the gaming mouse pad, it forms water droplets and slides down. Will not delay your work or gameplay.
  • 【Plug and play】: This mouse and keyboard pad is powered by USB, plug and play, no driver required. One button control light modes. Press one time to change the lighting mode, press twice to change the brightness, press and hold about 3 seconds turn ON/OFF.

Start a dependent database only after it is healthy

If the database runs in the same Compose project, give it a health check and make the web service depend on the healthy state:

services:
  web:
    depends_on:
      db:
        condition: service_healthy

  db:
    # Use a database image and pinned version supported by your application.
    healthcheck:
      test: ["CMD-SHELL", "the-database-health-command"]
      interval: 10s
      timeout: 5s
      retries: 5

Replace the health command with the database vendor’s readiness probe; for example, a PostgreSQL deployment commonly uses its native readiness utility. The important behavior is that Compose waits for the dependency’s health check before creating the dependent service. This prevents a web container from starting during the database’s initialization, but it does not replace application-level retry logic for later network interruptions.

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

Mount secrets explicitly

Compose secrets are exposed as files under /run/secrets/<secret_name> only to services that explicitly request them. A minimal arrangement is:

services:
  web:
    secrets:
      - flask_secret
      - db_password

secrets:
  flask_secret:
    file: ./secrets/flask_secret.txt
  db_password:
    file: ./secrets/db_password.txt

Create those files outside version control, restrict their permissions, and teach the application to read the file contents. For example, read /run/secrets/flask_secret at startup instead of putting the value in the Dockerfile. An .env.production file is useful for non-secret configuration, but it should also be protected and excluded from source control when it contains credentials.

Persist data deliberately

Containers are replaceable. Put database files, uploaded assets, or other state that must survive a rebuild on a named volume, or use an external managed database. Back up persistent data and periodically test restoration; neither Docker restart behavior nor a volume by itself is a backup.

Deploy and inspect the stack

  1. Copy the project, Dockerfile, production Compose file, environment configuration, and secret files to a controlled directory on the VPS. Keep secret files out of the Git repository.
  2. Build and start the production services:
    docker compose -f compose.production.yml up -d --build
  3. Check service state and health:
    docker compose -f compose.production.yml ps
    docker compose -f compose.production.yml logs -f web
  4. From the VPS, confirm that the loopback endpoint responds before involving DNS or TLS:
    curl -i http://127.0.0.1:8000/health
  5. If the health status is unhealthy, inspect the application logs, verify environment variables, and confirm that the health-check utility exists in the image.

A successful local response proves only that nginx can reach the container on the VPS. It does not prove that DNS, the public firewall, nginx routing, or certificate validation is correct.

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

Configure nginx as the reverse proxy

Install nginx on the VPS, then create a server block for the domain. The following configuration forwards requests to the loopback-published Gunicorn port and preserves the original host, client address, and scheme:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        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 the example hostnames with the names you actually control. Enable the site according to your nginx distribution layout, validate the complete configuration, and reload only after the test succeeds:

Rank #4
Sale
Vaydeer Wrist Rest for Keyboard and Mouse, Computer Ergonomic Wrist Support Pad, Soft Memory Foam Arm Cushion for Desk, Palm Hand Office Laptop Typing
  • 【Softer and More Comfortable】Vaydeer wrist rest has unique diamond pattern, which is the combination of softness and aesthetics. The materials of wrist rest are improved into higher quality memory foam and covered with silky smooth lycra. The computer wrist rest makes you as comfortable and cushiony as like rest your wrists on clouds.
  • 【Ergonomic Wrist Saver】The wrist rests for keyboard and mouse comes with a 17.32×3.15×0.83 inch keyboard wrist pad and a 5.94×3.15×0.83 inch mouse wrist support. Based on ergonomic design, the unique concave shape is the perfect fit for your wrist joints. The wrist rest pad fits most computer keyboards and laptops, improve hand and wrist posture, release your wrist and arm stress.
  • 【Non-Slip Rubber Bottom】Featuring an anti-skid silicone base on the bottom, this wrist keyboard support stays firmly in place on your desk, preventing the padding from sliding around, ensuring stable and consistent wrist support during extended computer sessions.
  • 【Better Experience & Pain Relief】Our keyboard arm rest is beneficial to alleviate the soreness caused by direct contact and friction between your arm and a hard desk surface, reducing the risk of wrist fatigue or carpal tunnel. The soft texture of memory foam can evenly distribute the pressure around your wrists and provide good support with just enough give.
  • 【Helpful in Multiple Scenarios】Whether you're working, studying, writing, typing, gaming, this keyboard and mouse rest combo is an essential accessory to add comfort and support to your hands and wrists. It’s also a great gift for men, women, family, friend, coworker, gamer, teacher, etc.
sudo nginx -t
sudo systemctl reload nginx

Flask describes an HTTP server placed in front of a WSGI server as a reverse proxy. Gunicorn’s deployment guidance also recommends a reverse proxy such as nginx or Apache and warns against running Gunicorn as root. nginx should remain the only service listening on public HTTP and HTTPS ports.

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

Trust forwarded headers carefully

Forwarded headers are supplied by nginx, so Flask must be configured to trust them only when requests can reach the application through that known proxy. If your application uses Flask’s proxy middleware, configure the number and identity of trusted proxies for this exact topology. Do not enable proxy-derived secure-cookie, scheme, or client-IP behavior while an untrusted network can send requests directly to Gunicorn.

Point DNS to the VPS, then enable HTTPS

Create and verify DNS records

DNS and TLS are separate operations. Create an A record for the domain pointing to the VPS IPv4 address and an AAAA record only if IPv6 is correctly configured and reachable. Wait until the records resolve to this server before requesting a certificate. Check both the bare domain and any www name listed in nginx.

Obtain a certificate with Certbot

After nginx serves the domain on port 80, install Certbot and its nginx integration from packages appropriate for your Ubuntu release. The documented automatic nginx flow is:

sudo certbot --nginx

Certbot can obtain the certificate and edit the nginx configuration to redirect HTTP to HTTPS. If you need to keep nginx edits under manual control, use the documented certificate-only variant:

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 certbot certonly --nginx

Follow the prompts for the exact domain names and contact address. Ensure the validation path is reachable from the Internet; a local curl request cannot substitute for an external ACME validation.

Test renewal

Certificate issuance is not the end of the task. Run the renewal test supplied by the installed Certbot package and fix any service, DNS, or permission error it reports. Confirm that the renewal timer or scheduled job remains enabled and that nginx reloads successfully after a renewed certificate.

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

Release, update, and rollback safely

Use a repeatable release sequence

  1. Tag or record the source revision and build a new image.
  2. Review configuration changes and confirm that required secrets are present.
  3. Run database migrations as an explicit one-off operation when your framework requires them; do not hide destructive migrations inside every container start.
  4. Recreate the service with the new image:
    docker compose -f compose.production.yml up -d --build
  5. Check docker compose ps, the health status, application logs, nginx errors, and a real HTTPS request.
  6. Retain the previous image tag and database recovery point until the release has passed its acceptance checks. If the new container fails, restore the prior image and configuration rather than repeatedly restarting a broken build.

Monitor the parts that restart policies cannot diagnose

Inspect disk usage, memory pressure, container restart counts, nginx error logs, certificate expiry, database availability, and application exceptions. Compose health checks can mark a service unhealthy and restart policies can bring a stopped container back, but neither tells you whether customers are receiving correct responses. Add external monitoring for the HTTPS endpoint and alert on sustained failures.

Best Value
Sale
Under Desk Cable Management Tray No Drill, Metal Mesh Cable Management Under Desk with Clamp Mount, 2 Hole Cable Tray with Wire Management and Cord Organizer for Home Office Standing Desk
  • 【No Drill Mounted】Cable management tray can be assembled in as little as 3 minutes or less-simply. Side clips easily clampe the edge of desk, pass through the holes on the sides, and secure excess cables with baskets. Desk cable management supports inward or outward installation to meet your needs in different scenarios, and provides you with great convenience when collecting and organizing wires.
  • 【Rubber Pad Protects Desk】Wire organizer under desk built-in soft rubber pads on the clamp protect your desk from scratches, so you will not damage your furniture or office.
  • 【Sturdy and Beautiful】Cord management under desk is made of high-quality carbon steel and holds about 15 lbs. The wire basket has 2 holes on each side of the tray to enable your cables to pass freely. Cable baskets are suitable for desk thickness use from 0.4" to 2.4".
  • 【Space Saving & Keep Organized】Desk cable management tray holds all your power cords, outlet strips, USB hubs and computer transformers hidden from your feet, and the data cables will not enter the vacuum cleaner to keep your desk clean and tidy. Desk wire organizer is also suitable for use in the kitchen and outdoors.
  • 【Reduce Safety Risks】Cable tray under desk keeps all plugs away from your kids, no more worry about tripping. Using 2 holes wire tray saves extra space, helps you easily cabling and wire protection. The mesh design is not easy to accumulate dust, but avoids safety accidents caused by messy wires.

Single container, Compose stack, or managed platform?

A single web container is adequate for a small stateless application, but the operational boundary changes as soon as you add a database, worker, cache, or scheduled job.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision axis Single web container Compose stack Managed hosting
Services One Flask/Gunicorn service, with dependencies external or absent. Multiple services such as web, database, worker, and proxy on a defined network. Platform-managed runtime and often managed database or proxy components.
Persistence Usually external; the web container should remain disposable. Named volumes or an external database, with backups and restore tests. Persistence, snapshots, and retention depend on the provider’s product.
Secrets Environment or mounted files must be protected on the VPS. Compose secrets can be granted per service and mounted under /run/secrets. Provider secret stores and access controls may be available.
Health and ordering Health checks and retries are your responsibility. Health checks plus service_healthy dependency conditions coordinate startup. Platform probes and rollout behavior vary by service.
Networking and TLS You maintain nginx, DNS, firewall rules, and certificates. You still maintain them unless the platform supplies the public proxy. The platform may terminate TLS and provide routing, but its networking model applies.
Upgrade and rollback Replace one image and retain the prior tag. Recreate related services in a controlled order and preserve migration recovery options. Use the platform’s deployment and rollback controls.
Operational burden Lowest initial complexity, but still requires patching and monitoring. More moving parts, with configuration kept in one reproducible project. Less server administration, in exchange for platform constraints and a different cost model.

Self-hosting is most appropriate when you want control over the Docker image, networking, and operating system and can maintain patches, backups, TLS, and monitoring. Managed hosting reduces server administration but changes portability, observability, scaling controls, and billing. Choose based on those responsibilities rather than on the Flask framework alone.

Troubleshoot common failures

nginx returns 502 Bad Gateway

  • Confirm the web container is running and healthy with docker compose -f compose.production.yml ps.
  • Read the application log with docker compose -f compose.production.yml logs web.
  • Verify that nginx points to 127.0.0.1:8000 when the port is loopback-published, or to web:8000 when nginx is on the Compose network.
  • Run curl http://127.0.0.1:8000/health on the VPS. A failure here is a container or Gunicorn issue, not a DNS problem.

The container exits immediately

Inspect the first startup error rather than restarting blindly. Common causes are an incorrect Gunicorn module or factory target, a missing dependency, an unset required environment variable, or an application import that performs work before configuration is loaded. Run the image interactively with the same environment and verify the import path.

The database is unavailable during startup

Add a real database health check and use depends_on with condition: service_healthy. Keep application-level connection retries as well, because a healthy database can become unavailable after Compose has started every service.

Certbot cannot validate the domain

Confirm that the A and, if used, AAAA records resolve to this VPS. Check that nginx is listening publicly on port 80, the requested hostname appears in server_name, and the validation path is not blocked by the firewall or another proxy. Correct DNS or nginx first, then rerun Certbot.

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

Firewall behavior is surprising

Check Docker’s published-port rules instead of assuming ufw alone describes exposure. Remove unintended port mappings, bind internal services to loopback or an internal network, and retest from outside the VPS.

Flask reports the wrong scheme or client IP

Verify that nginx sends Host, X-Forwarded-For, and X-Forwarded-Proto. Then configure Flask’s trusted-proxy handling for the actual number of proxies. Do not trust arbitrary forwarded headers from direct clients.

Final deployment checklist

  • Gunicorn, not Flask’s development server, runs the application.
  • The Gunicorn container binds to its container interface but is not publicly exposed.
  • nginx is the only public HTTP/HTTPS entry point and passes the original host and scheme.
  • The production Compose file has no development source bind mount, uses production configuration, and defines a restart policy.
  • Health checks cover the web service and any database that must be ready before it starts.
  • Secrets are excluded from the image and repository and are mounted only into services that need them.
  • Persistent data has a backup and restoration procedure.
  • DNS resolves to the VPS before Certbot runs, and renewal testing succeeds.
  • Firewall rules and Docker-published ports have been reviewed together.
  • Every release has a recorded image, migration plan, health check, log review, and rollback path.

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.