Back to Blog
Tutorial12 min read2026-07-31

Deploying a FastAPI App with Managed KV Storage

Wire a managed Redis KV store to your FastAPI app for caching and session storage—automatic KV_URL injection, HTTP REST API access, and zero-config TLS.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

FastAPI is a modern Python web framework optimized for APIs, but when you need caching, rate limiting, or session storage, you need a key-value store. PandaStack provisions a managed Redis instance and injects connection variables into your app automatically, so you can start caching responses without provisioning infrastructure or managing connection strings.

This post shows how to deploy a FastAPI app with a linked KV store, how to connect with both the native Redis client and the HTTP REST API, and how to handle connection pooling under load.

What is the KV store

PandaStack's KV store is a managed Redis instance, provisioned independently of the SQL databases (PostgreSQL and MySQL). When you link it to a container app, three environment variables are injected:

  • KV_URL: Redis connection string (redis://default:password@host:6379)
  • KV_REST_API_URL: HTTP endpoint for REST-based access
  • KV_REST_API_TOKEN: Bearer token for the REST API

You can connect with any Redis client using KV_URL, or use the REST API for serverless/edge environments where persistent TCP connections aren't available.

A minimal FastAPI app

Start with a basic FastAPI app:

# app.py
from fastapi import FastAPI

app = FastAPI()

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

Install dependencies:

pip install fastapi uvicorn[standard]

Run it locally:

uvicorn app:app --host 0.0.0.0 --port 8000

Critical: bind to 0.0.0.0, not 127.0.0.1. Container deployments route traffic via the pod IP, and binding to localhost makes the app unreachable. This is the most common reason FastAPI apps deploy successfully but don't respond to requests.

Adding Redis for caching

Install the redis client:

pip install redis

Create a Redis client that reads KV_URL:

# app.py
import os
import redis
from fastapi import FastAPI

app = FastAPI()

# Connect to Redis using KV_URL
kv_url = os.getenv("KV_URL")
if kv_url:
    redis_client = redis.from_url(kv_url, decode_responses=True)
else:
    redis_client = None

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

@app.get("/cached")
def cached_route():
    if not redis_client:
        return {"error": "KV store not configured"}

    cached = redis_client.get("cached_value")
    if cached:
        return {"value": cached, "source": "cache"}

    # Simulate expensive computation
    value = "computed_value"
    redis_client.setex("cached_value", 60, value)  # Cache for 60 seconds
    return {"value": value, "source": "computed"}

This route checks Redis for cached_value. If it exists, the cached value is returned. Otherwise, the value is computed and stored in Redis with a 60-second TTL.

Deploying the app

Create a requirements.txt:

fastapi
uvicorn[standard]
redis

Add a pandastack.json to specify the start command:

{
  "type": "container",
  "language": "python",
  "startCommand": "uvicorn app:app --host 0.0.0.0 --port $PORT",
  "healthCheckPath": "/"
}

The $PORT variable is injected by PandaStack (defaults to 8000). Uvicorn reads it from the environment.

Push the repo to GitHub, then deploy via the API:

curl -X POST https://api.pandastack.io/v1/projects \
  -H "Authorization: Bearer psk_live_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "container",
    "name": "fastapi-kv",
    "repositoryName": "yourusername/fastapi-kv",
    "branch": "main",
    "autoDeploy": true
  }'

The response includes a projectId. Save it.

Provisioning the KV store

Create a Redis instance via the dashboard (Databases → KV Stores → Create) or the API:

curl -X POST https://api.pandastack.io/v1/kv-stores \
  -H "Authorization: Bearer psk_live_your_token" \
  -H "Content-Type: application/json" \
  -d '{ "name": "fastapi-cache" }'

The response includes the KV_URL and REST API credentials.

Linking the KV store to the app

Link the KV store in the dashboard: go to the project's Environment tab, click Link KV Store, and select the Redis instance. This injects KV_URL, KV_REST_API_URL, and KV_REST_API_TOKEN and redeploys the app.

Alternatively, set the environment variables manually:

curl -X POST https://api.pandastack.io/v1/projects/42/env \
  -H "Authorization: Bearer psk_live_your_token" \
  -H "Content-Type: application/json" \
  -d '{
    "env": [
      { "name": "KV_URL", "value": "redis://default:pass@host:6379" }
    ]
  }'

Then trigger a redeploy:

curl -X POST https://api.pandastack.io/v1/projects/42/deploy \
  -H "Authorization: Bearer psk_live_your_token"

The next deploy reads KV_URL at runtime and connects to the managed Redis instance.

Using the HTTP REST API

If you're deploying to an edge environment or want to avoid managing TCP connections, use the HTTP REST API instead of the native Redis client:

import os
import httpx
from fastapi import FastAPI

app = FastAPI()

kv_rest_url = os.getenv("KV_REST_API_URL")
kv_rest_token = os.getenv("KV_REST_API_TOKEN")

@app.get("/cached-rest")
async def cached_rest_route():
    if not kv_rest_url or not kv_rest_token:
        return {"error": "KV REST API not configured"}

    headers = {"Authorization": f"Bearer {kv_rest_token}"}

    # GET key
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{kv_rest_url}/get/cached_value", headers=headers)
        if resp.status_code == 200 and resp.json().get("result"):
            return {"value": resp.json()["result"], "source": "cache"}

        # SET key with 60s TTL
        value = "computed_value"
        await client.post(
            f"{kv_rest_url}/set/cached_value",
            headers=headers,
            json={"value": value, "ex": 60}
        )
        return {"value": value, "source": "computed"}

Install httpx:

pip install httpx

The REST API is compatible with Upstash's protocol, so you can use Upstash client libraries if you prefer.

Connection pooling

The native redis-py client uses a connection pool by default. If you deploy multiple replicas of your app, each replica opens its own pool (default: up to 50 connections per pool). Redis can handle thousands of connections, but if you scale to many replicas, you may hit limits.

To limit connections per replica, configure the pool size:

redis_client = redis.from_url(
    kv_url,
    decode_responses=True,
    max_connections=10
)

With 5 replicas and 10 connections each, you use 50 total connections.

The HTTP REST API doesn't use persistent connections, so connection pooling isn't a concern. Each request is a short-lived HTTP call.

Testing the API

Once deployed, the dashboard shows the live URL (https://fastapi-kv-xyz.pandastack.app). Test the caching route:

curl https://fastapi-kv-xyz.pandastack.app/cached
# {"value":"computed_value","source":"computed"}

curl https://fastapi-kv-xyz.pandastack.app/cached
# {"value":"computed_value","source":"cache"}

The first request computes the value and caches it. The second request returns the cached value. After 60 seconds, the cache expires, and the next request recomputes.

redis-cli caveat

The redis-cli tool does not send TLS SNI (Server Name Indication), so it cannot connect directly to PandaStack's managed Redis. Use a Redis client library instead, or use the dashboard's built-in Redis shell (Database → KV Store → Shell).

If you need redis-cli for debugging, connect via a TLS proxy like stunnel. The PandaStack docs have a guide for this.

Why this matters

Managed Redis is offered by AWS (ElastiCache), Azure (Cache for Redis), and Upstash. But ElastiCache requires VPC setup, Azure's offering is expensive for small workloads, and Upstash charges per request. PandaStack's KV store runs in the same Kubernetes cluster as your app, so latency is sub-5ms and there's no per-request charge.

The HTTP REST API makes it usable in serverless and edge environments where persistent TCP connections aren't available. This is the same pattern Upstash uses, and it's compatible with their client libraries.

Common use cases

Session storage: Store user sessions in Redis instead of a SQL database, reducing database load.

Rate limiting: Track API request counts per IP or user with INCR and EXPIRE.

Job queues: Use Redis lists or streams to queue background jobs (pair with Celery for Python).

Leaderboards: Use sorted sets (ZADD, ZRANGE) to maintain real-time leaderboards.

Cache invalidation: Store computed values with TTLs, invalidate manually with DEL.

Full project structure

fastapi-kv/
├── app.py              # FastAPI app + Redis client
├── requirements.txt    # Dependencies
├── pandastack.json     # Deploy config
└── README.md

requirements.txt:

fastapi
uvicorn[standard]
redis
httpx

pandastack.json:

{
  "type": "container",
  "language": "python",
  "startCommand": "uvicorn app:app --host 0.0.0.0 --port $PORT",
  "healthCheckPath": "/",
  "env": [
    {
      "key": "KV_URL",
      "description": "Redis connection string (link a KV store in the dashboard)"
    }
  ]
}

Push this to GitHub, create the project, link the KV store, and the app is live with caching enabled.

References

  • [FastAPI Documentation](https://fastapi.tiangolo.com/)
  • [redis-py Documentation](https://redis-py.readthedocs.io/)
  • [Upstash REST API](https://upstash.com/docs/redis/features/restapi)
  • [PandaStack KV Store Documentation](https://docs.pandastack.io/databases/kv-store)

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also