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

The reliable production pattern is a two-service Compose stack: a FastAPI container listens only on the private Docker network, while a reverse proxy owns ports 80 and 443 and terminates HTTPS. On a VPS, you build the image, set secrets in an environment file, point DNS at the server, start the stack with docker compose up -d, and verify the proxy and application logs.

This guide uses Caddy because a single-domain setup needs little configuration. It also explains when Traefik or Nginx is a better fit, how to scale FastAPI correctly, and how to recover from common deployment failures.

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

What the finished architecture looks like

Internet traffic reaches the VPS on TCP ports 80 and 443. Caddy receives those requests, obtains and renews the TLS certificate for your hostname, and forwards requests over the private Compose network to the service named api. The FastAPI process listens on port 8000 inside its container; that port is not published on the VPS.

Client (HTTPS)
       |
       v
VPS:80/443  ->  caddy  ->  api:8000 (private Compose network)

This separation prevents clients from bypassing HTTPS or the proxy by connecting directly to the application. The official FastAPI container guidance recommends one Uvicorn process per container and scaling by running more containers when needed, rather than placing several workers in one container. See FastAPI’s Docker deployment guidance for the current official pattern.

#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:Upgraded 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.
  • Warranty & Support: Our Electric air duster comes with 5 years warranty and 24/7 customer service. During this period, if you have any questions,pls kindly feel free to contact us.

Prerequisites and decisions

  • A Linux VPS with a public IPv4 address (and IPv6 if you intend to serve it).
  • A DNS name such as api.example.com that you control.
  • SSH access with a non-root administrative account.
  • Docker Engine and the Docker Compose plugin installed from your VPS distribution or Docker’s current instructions.
  • A FastAPI project that can start locally.
  • Ports 80 and 443 available for the proxy. Do not run another web server on those ports.

The exact Docker and Compose package names vary by distribution. Confirm the installed versions with docker --version and docker compose version before deploying. Keep firewall and SSH-hardening steps aligned with your VPS provider’s current security documentation; they are separate from the Compose configuration below.

Create the FastAPI application

Application module

A minimal project can use this layout:

fastapi-vps/
├── app/
│   └── main.py
├── requirements.txt
├── Dockerfile
├── .dockerignore
├── compose.yml
├── Caddyfile
└── .env

Put the application object in app/main.py:

from fastapi import FastAPI

app = FastAPI(title="Example API")

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

@app.get("/")
def read_root():
    return {"message": "FastAPI is running"}

Python dependencies

For the official FastAPI CLI used in the Dockerfile, list the standard extra in requirements.txt:

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

For a real service, pin versions after testing them together and update them deliberately. There is no specific compatible version set for this example, so do not copy a version number from an unrelated tutorial.

Dockerfile

Use a small Python base image and run one FastAPI process. The official example uses fastapi run app/main.py --proxy-headers --port 80; this version keeps the same command and flag but chooses port 8000 so it matches the Compose network:

FROM python:3.12-slim

WORKDIR /code

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

COPY app ./app

EXPOSE 8000

CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "8000"]

--proxy-headers allows FastAPI/Uvicorn to use the original scheme and client information supplied by the TLS-terminating proxy. It is appropriate here because only Caddy can reach the app on the private network. Never publish the app directly while blindly trusting arbitrary forwarded headers.

Keep build context clean

Create .dockerignore so local caches, credentials, and a virtual environment are not copied into the image:

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.
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
.git
.gitignore
__pycache__/
*.py[cod]
.venv/
venv/
.env
.pytest_cache/

Keep configuration and secrets out of the image

Compose can load variables from an environment file with env_file. Create .env on the VPS (and keep it out of version control):

APP_ENV=production
# Add application-specific values here, for example:
# DATABASE_URL=...
# SECRET_KEY=...

Compose substitutes variables in the YAML file from the project environment, while env_file passes the listed values into the container. If one value needs an explicit override, set it under the service’s environment section; inline values take precedence over the file.

Restrict the file so only the deployment account can read it, for example with chmod 600 .env. Do not bake secrets into a Dockerfile layer, commit them to Git, or paste them into a public issue. Environment variables are convenient, but highly sensitive installations may eventually need a dedicated secret manager.

Compose the API and HTTPS proxy

Production Compose file

Save this as compose.yml:

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - api

  api:
    build: .
    restart: unless-stopped
    env_file:
      - .env
    expose:
      - "8000"

volumes:
  caddy_data:
  caddy_config:

Only Caddy has a ports mapping. The API’s expose declaration documents its internal port and makes it available to other services on the Compose network without publishing it on the host. restart: unless-stopped brings both services back after a crash or VPS reboot, but it does not fix an image that repeatedly fails to start; inspect the logs in that case.

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

The named volumes preserve Caddy’s certificate data and configuration across container recreation. The sample is illustrative rather than an executed deployment, so validate the syntax with docker compose config on your server before starting it.

Caddyfile

Create Caddyfile with your real DNS name:

api.example.com {
    reverse_proxy api:8000
}

Replace api.example.com with the hostname whose A (and, if used, AAAA) record points to this VPS. Caddy listens on 80 and 443, uses the shared Compose network to resolve api, and stores certificate state in the mounted /data volume. Certificate issuance requires that the hostname resolve publicly and that inbound HTTP/HTTPS traffic reach the VPS; a firewall, cloud security group, or another process blocking either port will prevent issuance or renewal.

Rank #3
BladeHawks RGB Gaming Mouse Pad Extra Large Extended 31.5x12 Inch-Black
  • 【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.

Validate locally before touching the VPS

  1. From the project directory, run docker compose config. Fix YAML, variable-substitution, or missing-file errors before building.
  2. Build and start the services with docker compose up --build. Keep this foreground session open for the first run.
  3. In a second terminal, check docker compose ps and read docker compose logs -f api.
  4. Because the API is not published to the host, test through Caddy after adding a temporary local DNS entry or a hosts-file mapping for your chosen hostname. A successful response from /health should be {"status":"ok"}.
  5. Stop the stack with docker compose down when the local check is complete. Do not use docker compose down -v unless you intentionally want to delete the named certificate volumes.

depends_on controls startup order, not application readiness. Caddy may start before FastAPI has finished importing; it should recover once the upstream is listening, but logs are the authority.

Deploy the stack to a VPS

1. Point DNS at the server

Create an A record for the API hostname pointing to the VPS IPv4 address. Add an AAAA record only if the VPS is correctly configured for IPv6. DNS propagation can be gradual; verify resolution from a machine outside the VPS before requesting a certificate.

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

2. Install Docker and Compose

Install Docker Engine and the Compose plugin using current instructions for the VPS operating system. Confirm that your deployment user can run Docker, either through the distribution’s Docker group configuration or with the appropriate administrative escalation. Avoid mixing an obsolete standalone docker-compose binary with the current docker compose plugin unless you have a specific compatibility reason.

3. Copy the project

Transfer the source, Dockerfile, Compose file, Caddyfile, and requirements file to a directory such as /srv/fastapi-vps. Create .env directly on the server or transfer it through a protected channel, then set restrictive permissions:

cd /srv/fastapi-vps
chmod 600 .env
docker compose config

Review the rendered configuration and make sure no secret is accidentally being interpolated into a public image or a published port.

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.

4. Build and start in the background

docker compose up -d --build

The first build downloads the base image and Python dependencies. Subsequent deployments reuse unchanged layers. Check both services:

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.
docker compose ps
docker compose logs --tail=100 api
docker compose logs --tail=100 caddy

5. Test the public endpoint

Once DNS resolves, request https://api.example.com/health from an external machine. Confirm that the certificate hostname matches, the response is from your application, and HTTP requests are redirected or otherwise handled by the proxy’s normal HTTPS behavior. If Caddy cannot obtain a certificate, inspect its logs first; DNS, blocked ports, rate limits, and an incorrect hostname are the usual classes of failure.

6. Make updates repeatable

Deploy a tested change by copying the updated source and running:

docker compose up -d --build

Compose recreates only services whose image or configuration changed. Follow the rollout with docker compose ps and targeted log checks. Keep a known-good image or source revision so you can roll back by rebuilding that revision if a release fails.

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

Choose the reverse proxy that fits the VPS

Proxy Best fit Configuration model Important details
Caddy One or a few domains with the least setup effort A concise Caddyfile Use the official caddy:2 image, publish 80 and 443, mount the Caddyfile read-only, and persist /data and /config.
Traefik Many Compose services whose routes change frequently Docker labels read by the proxy Set exposedByDefault to false and add traefik.enable=true only to intended services. A commonly documented Traefik v3 pattern mounts the Docker socket read-only and persists an acme.json file for Let’s Encrypt; verify current directives before production use.
Nginx Teams that want explicit, manual proxy and static-file control Handwritten server configuration It can terminate SSL and serve static files, but the material here does not provide a complete Nginx configuration. Treat any copied snippet as version-sensitive and test it with nginx -t in the chosen image.

Regardless of proxy choice, keep the same boundary: publish 80/443 from the proxy and leave FastAPI on the internal network. Do not publish 8000:8000 merely to make debugging convenient on a production VPS.

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

Processes, workers, and scaling

Default: one Uvicorn process per container

The official FastAPI guidance favors one process per container. This gives the container orchestrator a clear health and restart unit and lets you scale horizontally by adding API containers. A single VPS can run multiple replicas if CPU, memory, and your Compose setup justify it; place them behind the proxy and ensure shared state (uploads, sessions, and background jobs) is handled deliberately.

When Gunicorn is justified

If you explicitly need several worker processes inside one container, a common command is:

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.
gunicorn app.main:app -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000

Binding to 0.0.0.0 is required for traffic from outside the container. Choose worker counts from measured CPU and memory capacity rather than copying a formula. More workers increase memory use and can complicate graceful shutdown; they are not an automatic performance improvement.

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

The older tiangolo/uvicorn-gunicorn-fastapi-docker base image is deprecated. Its maintainer directs users to the official FastAPI Docker documentation instead, so build from a maintained Python base image and define the process explicitly. One third-party guide also says the uvicorn.workers module is deprecated in favor of the separate uvicorn-worker package, but that claim is not confirmed here against Uvicorn’s own documentation. Verify the worker package and import path against the versions you install before adopting this optional pattern.

Reliability and maintenance practices

  • Restart behavior: Keep restart: unless-stopped for both services so ordinary crashes and reboots recover automatically. It will not replace monitoring or fix configuration errors.
  • Persistent proxy state: Back up the Caddy data volume if you need to preserve certificate and proxy state during a server rebuild. Never delete it casually.
  • Application data: Add explicit volumes or an external object/database service for uploads and other state. Containers themselves are replaceable.
  • Health endpoint: Keep a cheap endpoint such as /health that does not depend on a slow downstream database unless your monitor is specifically testing that dependency.
  • Logs: Use docker compose logs -f api caddy during a release, then forward or rotate logs so an active service cannot fill the VPS disk.
  • Updates: Rebuild from a reviewed source revision, test locally, and record the image or Git revision deployed. Updating the base image or dependencies can change behavior even when application code is unchanged.
  • Backups: Back up databases and user files independently of the Compose project. Recreating containers does not restore external data.

Troubleshooting by symptom

Port 80 or 443 is already in use

Run ss -ltnp | grep -E ':(80|443)b' on the VPS to identify the process. Stop or reconfigure the competing web server, or deliberately choose a different architecture. Do not map Caddy to arbitrary public ports if you need normal HTTPS certificate validation and a conventional URL.

Caddy reports an upstream connection error

  • Run docker compose ps and confirm the API container is running.
  • Read docker compose logs api for import errors, missing environment variables, or a failed dependency installation.
  • Confirm the application listens on 0.0.0.0:8000 inside the container. The FastAPI CLI command above does this.
  • Ensure the Caddyfile uses the Compose service name api:8000, not localhost:8000. In a container, localhost means Caddy itself.

The certificate is not issued

  • Check that the DNS A/AAAA records resolve to this VPS from an external network.
  • Allow inbound TCP 80 and 443 in both the VPS firewall and any cloud security group.
  • Make sure no old proxy or test container owns those ports.
  • Read Caddy’s logs for the exact ACME response and correct the hostname or DNS before retrying.

FastAPI generates the wrong scheme or client address

Ensure the container command includes --proxy-headers when a trusted TLS proxy is in front. If users can reach the application directly, forwarded headers can be forged; the private-network design avoids that exposure.

The stack works until the VPS reboots

Check that both services have restart: unless-stopped, that Docker starts at boot, and that the Compose project is still in the same directory. Then inspect docker compose ps and the service logs. A missing .env file or an unavailable mounted path commonly causes an immediate restart loop.

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

Security checklist before opening the domain

  • Publish only the proxy’s 80 and 443 ports; do not publish the API port.
  • Use a dedicated deployment account and keep SSH authentication hardened according to your provider’s guidance.
  • Restrict .env permissions and rotate any credential that was exposed during testing.
  • Mount the Caddyfile read-only and the Docker socket read-only if a Traefik design requires it.
  • Keep images and Python dependencies updated through a tested release process.
  • Confirm that debug settings, interactive documentation exposure, and permissive CORS rules match your application’s actual requirements.
  • Back up databases, uploads, and other persistent data separately from containers and named volumes.

Production launch checklist

  1. DNS records resolve to the VPS.
  2. Docker Engine and the Compose plugin report the expected versions.
  3. docker compose config succeeds without leaking secrets.
  4. The API image builds and the /health endpoint works locally.
  5. Only Caddy publishes 80 and 443; the API uses expose.
  6. Caddy’s data and config volumes exist and are protected by backups.
  7. docker compose up -d --build completes, and both services remain running.
  8. The public HTTPS endpoint returns the expected response with a valid certificate.
  9. Logs, restart behavior, rollback source, and data backups are documented for whoever will operate the VPS.

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.