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

Freedom251 › Blog › Bottle

How to Deploy Python Web Apps with the Bottle Framework on a VPS

By Freedom251 Team · · 11 min read

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

Run Bottle behind a production WSGI server, keep it on a private port or Unix socket, put Nginx on the public HTTP/HTTPS ports, and let systemd restart the service and start it after reboots. Bottle’s built-in server is suitable for development and smoke tests, not for an internet-facing production workload.

What a production Bottle deployment looks like

A VPS deployment separates the application from the parts that must handle public traffic and operating-system lifecycle:

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Bottle provides the WSGI application object and routes.
  • Gunicorn or Waitress loads that object and manages worker processes or threads.
  • Nginx accepts connections on ports 80 and 443, serves suitable static files, and forwards dynamic requests to the private application listener.
  • systemd starts the WSGI server at boot, restarts it after a failure, and provides a consistent log and status interface.
  • The VPS firewall and DNS expose only the services that need to be public.

Bottle is deliberately small: it is distributed as a single-file module and has no dependencies beyond Python’s standard library. That makes it easy to deploy, but it does not change the need for a production WSGI server, a reverse proxy, process supervision, permissions, backups, and updates.

Before you install anything

Choose a Linux VPS and point DNS to it

Use a Linux VPS with a supported Python version, shell access, enough memory for the operating system and your chosen worker count, and a firewall you can configure. Create an A record for the application’s hostname pointing to the VPS public IPv4 address; add an AAAA record only when the server is correctly configured for IPv6. DNS changes can take time to appear, so verify resolution from a separate machine before blaming Nginx or the application.

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

Decide which hostname will serve the app, such as app.example.com. During initial setup you can test with the server’s address, but production TLS certificates and the final Nginx server block should use the real hostname.

Use a dedicated, unprivileged account

Create a service account that owns the application files and has no interactive administrative role. Bottle’s own tutorial strongly recommends not running a web application with root or administrator rights. The account should be able to read its code and virtual environment, write only to directories that genuinely need runtime data, and have no reason to modify system configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo useradd --system --create-home --home-dir /srv/mybottleapp --shell /usr/sbin/nologin bottleapp
sudo mkdir -p /srv/mybottleapp
sudo chown -R bottleapp:bottleapp /srv/mybottleapp

The exact user-management flags differ slightly between Linux distributions. Check the account’s home directory and ownership before continuing.

Create an importable Bottle application

A WSGI server imports a Python module and looks up an object. Keep that object at module scope and do not start Bottle’s development server when the module is imported.

from bottle import Bottle, request, response, static_file

app = Bottle()

@app.get('/')
def index():
    return {'status': 'ok', 'service': 'mybottleapp'}

@app.get('/healthz')
def healthz():
    response.content_type = 'text/plain; charset=UTF-8'
    return 'okn'

# Development only; do not execute this when Gunicorn or Waitress imports the module.
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=True, reloader=True)

Save this as /srv/mybottleapp/myapp.py. The command target will then be myapp:app: the text before the colon is the module name, and the text after it is the WSGI object. If your project uses Bottle’s default application, an equivalent target can expose application = default_app() and be started as module_name:application.

Do not call run() unconditionally at import time. If you do, the worker process can bind its own development listener, hang during import, or fail because the port is already in use.

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

Keep configuration outside the code where practical

Use environment variables or a root-readable, service-account-readable configuration file for deployment-specific values such as database endpoints and secret keys. Do not commit passwords or private keys to the application repository. Make the health endpoint cheap and deterministic so a local curl request and an external monitor can distinguish a running process from a broken dependency.

Build a virtual environment and lock dependencies

Install the system packages required by your distribution, then create the environment as the application account. Install Bottle and one production WSGI adapter into that environment, not into the system Python.

sudo -u bottleapp -H python3 -m venv /srv/mybottleapp/.venv
sudo -u bottleapp -H /srv/mybottleapp/.venv/bin/python -m pip install --upgrade pip
sudo -u bottleapp -H /srv/mybottleapp/.venv/bin/pip install bottle gunicorn
sudo -u bottleapp -H /srv/mybottleapp/.venv/bin/pip freeze > /srv/mybottleapp/requirements.lock

A lock file makes a rebuild repeatable. Review it into version control with your application, then recreate the environment from that file during a later deployment:

/srv/mybottleapp/.venv/bin/pip install --requirement /srv/mybottleapp/requirements.lock

Choose Waitress instead by installing waitress rather than gunicorn. Bottle’s server adapters are separate packages; installing Bottle alone does not install Gunicorn, Waitress, or another adapter.

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

Smoke-test the app before adding Nginx

Run the application on an unprivileged loopback port first. This isolates Python import and application errors from DNS, firewall, and proxy errors.

cd /srv/mybottleapp
/srv/mybottleapp/.venv/bin/gunicorn --workers 4 --bind 127.0.0.1:8000 myapp:app

From a second shell on the VPS, request the health route:

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

You should receive ok. If Gunicorn reports an import error, verify the working directory, module filename, object name, and installed packages. Stop the foreground process with Ctrl-C after the test; systemd will own the long-running process later.

The four-worker command is the form shown in Bottle’s deployment documentation. It is an example, not a capacity guarantee or a universal setting. Worker processes consume memory, and the useful number depends on your application’s blocking behavior, request mix, CPU, available RAM, and database limits. Start conservatively, observe the VPS, and change the count deliberately.

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

Choose Gunicorn, Waitress, or Nginx Unit

All three can serve a Bottle WSGI application, but they fit different operational preferences. No official source establishes a universal performance winner, so choose based on the way you operate the VPS.

Option Concurrency model and deployment fit What you must configure When it is a sensible choice
Gunicorn Pre-fork worker model with extensive process and deployment controls. Install it separately, select workers and a bind address, and supervise it with systemd. The official guide documents service units, socket units, Unix-socket listening, and Nginx proxying. A common Linux choice when you want independent worker processes and well-documented systemd integration.
Waitress Multi-threaded WSGI adapter that runs on common operating systems. Install the waitress package and start its waitress-serve command with your module and object. Useful when a threaded adapter or cross-platform operational consistency matters more than Gunicorn’s pre-fork model.
Nginx Unit An alternative application server and runtime with a Bottle-specific configuration approach. Install and configure Unit according to the operating system’s package and service procedures, then define the application entry point in Unit’s configuration. Reasonable when your team already operates Unit and wants its application configuration model instead of a separate Gunicorn or Waitress service.

Nginx is a reverse proxy, not a replacement for the WSGI adapter. In the Gunicorn and Waitress designs, Nginx handles public connections while the adapter executes Python code privately.

Run Gunicorn under systemd

A systemd unit gives the process a defined identity, directory, command, restart policy, and boot target. Create /etc/systemd/system/mybottleapp.service as root:

[Unit]
Description=Bottle application served by Gunicorn
After=network.target

[Service]
User=bottleapp
Group=bottleapp
WorkingDirectory=/srv/mybottleapp
Environment=PATH=/srv/mybottleapp/.venv/bin
ExecStart=/srv/mybottleapp/.venv/bin/gunicorn --workers 4 --bind 127.0.0.1:8000 myapp:app
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Reload systemd, start the service, and enable it for future boots:

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

ExecStart uses the virtual environment’s absolute Gunicorn path, so the service does not depend on an interactive shell’s PATH. WorkingDirectory ensures relative imports and relative file paths resolve from the project directory. Keep the service account’s write access narrow; if the application needs uploads, logs, or a cache, grant access only to those directories.

Use a Unix socket when you prefer filesystem permissions

Gunicorn can bind a Unix socket instead of a TCP loopback port. This keeps the application listener out of the TCP namespace and lets Nginx connect through a filesystem path. A socket-based ExecStart can look like this:

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

Ensure the socket directory exists at boot and that Nginx’s account can read and write the socket. A mismatch between socket ownership or mode and Nginx’s user produces a 502 response even when Gunicorn is healthy. A loopback address is simpler to troubleshoot; a Unix socket is not automatically faster or safer unless its permissions are correctly managed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Put Nginx in front of the application

Install Nginx from your Linux distribution, create a server configuration for the chosen hostname, and proxy only to the private listener. The following example uses the loopback port from the systemd unit:

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

    location /static/ {
        alias /srv/mybottleapp/static/;
        try_files $uri =404;
    }

    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 app.example.com with your hostname and adjust the static directory to match your project. With alias, the trailing slash matters: it maps the URL prefix to the filesystem directory. If the application does not have static files, omit that location instead of exposing an arbitrary project directory.

Enable the site using your distribution’s Nginx layout, validate the complete configuration, and reload:

sudo nginx -t
sudo systemctl reload nginx
curl --fail -H 'Host: app.example.com' http://127.0.0.1/healthz

The proxy headers preserve the original host, client address, forwarding chain, and protocol. Applications that generate absolute URLs or enforce HTTPS commonly need the forwarded protocol value. Do not trust arbitrary client-supplied forwarding headers when Nginx is not the only proxy in front of the service; define a trusted proxy arrangement appropriate to your network.

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

Open only the required network paths

The public firewall normally needs TCP 80 for HTTP and TCP 443 for HTTPS. Keep Gunicorn on 127.0.0.1 or a protected Unix socket so it is not reachable directly from the internet. SSH should be restricted to the addresses or access method your administration policy allows.

Test the path in layers:

  1. On the VPS, request Gunicorn directly with curl http://127.0.0.1:8000/healthz.
  2. On the VPS, request Nginx with the correct Host header.
  3. From an external network, resolve the hostname and request the public HTTP endpoint.
  4. After TLS is configured, request the HTTPS endpoint and confirm that the certificate hostname matches.

Add HTTPS and certificate renewal

Use the certificate issuance process supported by your VPS distribution or hosting environment. It should install the certificate and private key with restrictive permissions, add an Nginx TLS server block for the hostname, redirect HTTP to HTTPS where appropriate, and schedule automatic renewal. Verify renewal with the process’s dry-run or test mode rather than waiting for expiration.

After enabling TLS, the HTTPS server block should proxy to the same private Gunicorn or Waitress listener and retain the forwarding headers. Check that the application understands it is behind HTTPS through the forwarded protocol value before enabling secure-cookie or redirect logic. Keep port 80 available when the certificate renewal method requires an HTTP challenge, unless your chosen method uses another challenge type.

Deploy updates without losing control of the service

For a small VPS, a deliberate, observable release is safer than editing files while requests are arriving:

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.
  1. Copy or check out the new application revision into a release directory, or update the existing directory during a maintenance window.
  2. Update the lock file and install dependencies into the virtual environment.
  3. Run the local health check and any application tests as the service account.
  4. Check ownership and permissions, especially for uploaded files and configuration.
  5. Run sudo systemctl restart mybottleapp.service.
  6. Inspect sudo systemctl status mybottleapp.service and sudo journalctl -u mybottleapp.service -n 100 --no-pager.
  7. Request the health endpoint through Nginx, then test a representative application route.
  8. Reload Nginx only when its configuration changed; use sudo nginx -t before every reload.

A restart replaces the Gunicorn workers and is usually adequate for a small Bottle service. If you need near-zero interruption, design a socket or load-balancer handoff deliberately and test it; do not assume that changing the worker count in a running unit applies until systemd has restarted the process.

Troubleshoot by following the request path

The service will not start

  • Read the unit log with journalctl -u mybottleapp.service; import errors and syntax errors appear there.
  • Run the exact ExecStart command as bottleapp from /srv/mybottleapp to reproduce missing-package or permission failures.
  • Confirm that the WSGI target matches the filename and object, such as myapp:app.
  • Verify that the virtual environment still exists and that its Gunicorn executable is runnable.
  • Check that another process is not already listening on port 8000.

Nginx returns 502 Bad Gateway

  • Request the private listener directly. If it fails, fix Gunicorn or the Bottle import first.
  • Compare Nginx’s proxy_pass with Gunicorn’s bind address, port, or socket path.
  • For a Unix socket, inspect its directory, owner, group, and mode; Nginx must be able to connect.
  • Read the Nginx error log for connection-refused, permission-denied, and upstream-timeout details.
  • After changing either service, reload Nginx only after nginx -t succeeds and restart the WSGI service when its unit or command changed.

The domain shows the wrong site or does not resolve

  • Use a DNS lookup from outside the VPS and confirm the address matches the current server.
  • Ensure the Nginx server_name exactly matches the hostname the browser sends.
  • Check that the VPS firewall and provider firewall allow the public port.
  • Remember that an old DNS answer can remain cached until its TTL expires.

Static files return 403 or 404

  • Confirm that the requested file exists at the path produced by the alias mapping.
  • Give Nginx traverse permission on each parent directory and read permission on the files without granting it write access.
  • Check whether the URL includes the expected trailing slash and prefix.
  • Remove the static location temporarily and proxy the route to Bottle if the application, rather than Nginx, is meant to generate the response.

The app works locally but fails after a reboot

  • Check systemctl is-enabled mybottleapp.service and its boot log.
  • Use absolute paths in ExecStart, configuration, and file references.
  • Do not rely on shell activation commands; systemd should call the virtual environment’s binary directly.
  • Confirm that DNS, Nginx, and the firewall are configured independently of your login session.

Operational checklist

  • Application code exposes an importable WSGI object and does not call run() during import.
  • Bottle and the selected WSGI adapter are installed in a virtual environment.
  • Dependencies are recorded in a locked requirements file.
  • A dedicated unprivileged account owns the application.
  • Gunicorn or Waitress listens only on loopback or a correctly permissioned Unix socket.
  • systemd starts the service, restarts failures, and is enabled for boot.
  • Nginx proxies dynamic requests and serves only explicitly intended static paths.
  • Proxy headers are passed consistently, and the application’s HTTPS behavior is tested.
  • DNS points to the VPS, the firewall exposes only required ports, and TLS renewal is automated.
  • Logs, backups, snapshots, and monitoring are part of the VPS operating routine rather than an afterthought.

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.

BottleGunicornNGINXPythonsystemdVPS deploymentWaitress

Leave a Reply

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

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

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

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

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