Skip to content

Limiter auto-loads .env file causing Docker permission errors #256

@detrax

Description

@detrax

Describe the bug

I ran into an issue where Limiter tries to read a .env file even when I'm already passing environment variables through Docker. My container doesn't mount .env (and shouldn't need to - I'm using docker run --env-file like the Docker docs recommend), but slowapi still attempts to load it through starlette's Config class.

The problem is that starlette's Config defaults to loading .env when you don't explicitly tell it not to. In production containers where environment variables come from the orchestration layer (Docker, Kubernetes, etc.) and there's no .env file to read, this causes warnings and can even lead to startup failures depending on file permissions

To Reproduce

Minimal example:

from slowapi import Limiter
from slowapi.util import get_remote_address
import os

# Environment variables already set (e.g., via docker run --env-file)
os.environ['RATE_LIMIT_STORAGE'] = 'memory://'

# This tries to read .env file, causing permission errors if file doesn't exist
# or has wrong permissions in Docker
limiter = Limiter(
    key_func=get_remote_address,
    storage_uri=os.getenv("RATE_LIMIT_STORAGE"),
    default_limits=["1000 per hour"]
)

Docker context:

# Dockerfile - typical production setup
FROM python:3.13-slim
WORKDIR /app
COPY . .
RUN pip install slowapi fastapi
CMD ["python", "app.py"]
# Run with environment variables via --env-file (best practice)
docker run --env-file config.env myapp

# Error: starlette.config tries to open('.env') which doesn't exist
# UserWarning: Config file '.env' not found.

Error message:

/usr/local/lib/python3.13/site-packages/starlette/config.py:61: UserWarning: Config file '.env' not found.
  warnings.warn(f"Config file '{env_file}' not found.")

Expected behavior

I'd expect that when my environment variables are already set (via --env-file, Kubernetes ConfigMaps, etc.), slowapi wouldn't try to read any files. Just read from os.environ and call it a day - no file I/O needed at initialization.

Current Workaround

I got it working by setting config_filename="/dev/null":

limiter = Limiter(
    key_func=get_remote_address,
    storage_uri=os.getenv("RATE_LIMIT_STORAGE"),
    default_limits=["1000 per hour"],
    config_filename="/dev/null"  # Prevent auto-loading .env
)

It works, but it's not obvious that you need to do this, and I only found it by digging through the source code.

Your app

  • Framework: FastAPI 0.115.6
  • slowapi version: 0.1.9 (latest)
  • Python version: 3.13
  • Environment: Docker container (production)

Additional context

Looking at the code, Limiter.__init__ calls Config(env_file=config_filename) where config_filename defaults to ".env", which triggers starlette to try loading the file.

I noticed PR #170 documents the config_filename parameter, but doesn't change the default behavior. This hits anyone running containers the recommended way (environment variables via orchestration, no .env file mounted).

Some possible fixes:

  1. Default config_filename to None and document that users who want .env loading should explicitly set it
  2. Only try loading .env if it exists and is readable
  3. Follow starlette 1.0+ where env_file is opt-in rather than default

This affects any production deployment following 12-factor app config principles - environment variables should just work without needing workarounds or file mounts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions