KAHIBARO
Discord Login Register

22.1. Application Servers

Why You Need an Application Server

When you run a backend application in production, you do not usually expose your framework directly to the internet. Instead, you run it inside a dedicated application server process.

An application server is a program that:

It sits between your code and the outside world, often behind a reverse proxy such as Nginx or Traefik.

Typical Python examples are Uvicorn and Gunicorn. In this chapter we focus on the general ideas that apply to application servers and will look at specific ones in the next chapters.

Key idea: Do not run your development server in production. Always use a proper application server that is designed for concurrency, robustness, and performance.


Application Server vs Development Server

Most backend frameworks come with a development server:

These are convenient but are not tuned or hardened for production.

Typical differences:

FeatureDevelopment ServerApplication Server
PerformanceBasicOptimized, often supports async and workers
ConcurrencyOften single process, maybe single threadMultiple workers, async event loops, threads
ReliabilityCrashes easily, not self-healingDesigned to handle load and errors gracefully
Hot reloadYes, auto restart on code changeNo, must be restarted via process manager
SecurityMinimalConfigurable limits, timeouts, and isolation
Use caseLocal developmentStaging and production

You should:

Where Application Servers Fit in the Stack

A typical production web stack looks like this:

  1. Client
    Browser, mobile app, or another service.
  2. Reverse proxy (Nginx / Traefik)
    • Terminates HTTPS
    • Handles TLS certificates
    • Routes requests to the correct backend service
    • Can serve static files directly
  3. Application server (Uvicorn / Gunicorn / Uvicorn + Gunicorn combo)
    • Speaks HTTP (or ASGI/WSGI internally)
    • Manages worker processes or async event loops
    • Runs your application code for each request
  4. Your application code
    • Framework such as FastAPI, Django, Flask
    • Contains your business logic
  5. Databases, caches, external services
    • PostgreSQL, Redis, third‑party APIs, etc.

Example flow:

  1. User hits https://api.example.com/users.
  2. Nginx receives the encrypted request on port 443, decrypts it, and forwards it (for example) to http://127.0.0.1:8000.
  3. The application server listens on 127.0.0.1:8000, chooses a worker, and invokes your application.
  4. Your application queries the database, builds a response, and returns it to the server.
  5. The application server sends the HTTP response back to Nginx, which sends it back to the client.

Concurrency Models in Application Servers

One of the important jobs of an application server is to handle many requests at the same time. There are several models.

1. Single process, single thread

This is the simplest model:

This is common in simple development servers. It is not suitable for real-world traffic.

2. Multi process

The server starts multiple worker processes:

Example configuration idea:

Each worker can handle multiple requests over time. If one worker crashes, the master can start a new one.

3. Multi thread

The server starts threads inside a single process:

Threads share memory, so they can be faster to start. But bugs can affect all threads, and some languages have limitations with threads.

4. Asynchronous / event loop

In async servers:

This is very useful for I/O‑bound web apps, which is common in backend development.

Many modern application servers support combinations, for example:

Rule of thumb: CPUs are good at running multiple processes, and async is good at handling many I/O-bound requests. Application servers usually combine these techniques for better throughput.


Worker Processes and How to Choose Their Number

An application server usually exposes a parameter like --workers to set how many worker processes to run.

A common heuristic:

Recommended worker formula:
For CPU‑bound workloads:
$$ \text{workers} \approx 2 \times \text{CPU\_cores} $$
For I/O‑bound workloads:
$$ \text{workers} \approx 2 \times \text{CPU\_cores} + 1 $$
Adjust based on memory and real-world testing.

Example:

You then:

  1. Run load tests.
  2. Monitor CPU usage, memory usage, and response times.
  3. Increase or decrease worker count.

If you choose too few workers:

If you choose too many workers:

Timeouts, Keep‑Alive, and Connection Handling

Application servers need careful configuration of timeouts and connections.

Typical parameters:

Example situations:

You will often configure timeouts both:

They must be compatible. For instance, if the proxy closes connections after 10 seconds but your app server expects 60 seconds, long responses might be cut off by the proxy.


Graceful Shutdown and Restart

When you deploy new versions or stop your server, you want a graceful shutdown:

  1. The server stops accepting new connections.
  2. Existing requests are allowed to finish up to a limit.
  3. Workers are then stopped.
  4. Resources such as database connections are cleaned up.

Benefits:

Application servers usually:

Example scenario:

For rolling or zero-downtime deployments, you often run two or more instances and restart them one at a time, so there is always at least one healthy instance behind the reverse proxy.


Handling Errors at the Server Level

Your application will raise exceptions, time out, and sometimes crash. A good application server:

Common error-related settings include:

Example idea:

You configure a server so that:

If an attacker tries to send a 500 MB POST body, the server can reject the request early, without your application trying to read it all into memory.


Application Servers and Protocols (HTTP, WSGI, ASGI)

Application servers speak two sides:

  1. External side:
    HTTP over TCP connections from clients or a reverse proxy.
  2. Internal side:
    A protocol specific to your application type, for example:
    • WSGI for classic synchronous Python web applications.
    • ASGI for asynchronous Python applications and WebSockets.
    • Other ecosystems have their own standards.

Your application is usually a callable that the server invokes according to that protocol.

Simplified idea:

You rarely need to implement these protocols yourself, but you should understand:

This matters when you choose a server for a specific framework.


Example: Running an Application Server Locally (Conceptual)

Imagine you have a file main.py:

python
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
    return {"message": "Hello from production-style server"}

Using an application server might look like:

bash
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

Even without going into Uvicorn specifics yet, note what happens conceptually:

If your code crashes in one worker, the server can replace that worker.


Application Servers Behind a Reverse Proxy

Although some application servers can listen directly on the public network interface, in production you usually:

Benefits:

Conceptual Nginx snippet:

nginx
location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Nginx receives the request, then passes it to your application server at 127.0.0.1:8000.


Monitoring and Observing Application Servers

To keep your backend healthy, you need to observe how your application server behaves.

Useful metrics and logs:

Metric / LogWhy it matters
Requests per secondShows traffic volume
Average and percentile latencyShows how fast responses are
Error rate (5xx responses)Shows instability
Worker count and worker restartsDetects crashes or memory leaks
CPU and memory usage per processHelps choose worker counts and instance sizes

Application servers often have:

You will combine these with external tools for logging and monitoring in later chapters.


Summary

In this chapter you learned what application servers are and why they are critical for production backend systems:

In the next chapters you will see how to use Uvicorn and Gunicorn specifically as Python application servers, and how to combine them with reverse proxies in real deployments.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!