Django-Secrets mit einer .env-Datei verwalten

  • 9. Juli 2026

Datenbankpasswörter, API-Keys und der Django SECRET_KEY haben eines gemeinsam: Sie gehören nicht in den Quellcode. In dieser Anleitung zeige ich, wie du Secrets, wie sie genannt werden, mit einer .env-Datei und dem Package python-dotenv sauber aus dem Code heraushältst. Der Ansatz ist minimal, funktioniert in jedem Python-Projekt und ist ohne Umbau produktionstauglich.

Die Django-Dokumentation zeigt bei den PostgreSQL-Einstellungen ein Beispiel mit einer Service-Datei (.pg_service.conf) und einer Passwort-Datei (.pgpass). Das sind PostgreSQL-eigene Konfigurationsdateien im Home-Verzeichnis des Benutzers. Sie funktionieren, sind aber auf PostgreSQL beschränkt. Für die meisten Django-Projekte gibt es eine einfachere und universellere Variante, und genau die schauen wir uns hier an.

Warum Secrets nicht in den Code gehören

Drei Gründe sprechen dagegen, Passwörter und Keys direkt in settings.py zu schreiben:

  • Sie landen im Git-Repository und sind damit für jeden sichtbar, der Zugriff auf das Repo hat.
  • Entwicklung, Staging und Produktion brauchen unterschiedliche Werte. Fest einprogrammierte Secrets stehen dem im Weg.
  • Auch in einem privaten Repository bleibt ein Secret im Code ein Sicherheitsrisiko. Ein Repo wird geforkt, kopiert oder versehentlich öffentlich gestellt, schneller als einem lieb ist.

Die Lösung: Secrets kommen aus Umgebungsvariablen, nicht aus dem Code.

Was sind Umgebungsvariablen?

Jeder Prozess auf einem Betriebssystem hat Zugriff auf einen Pool von Schlüssel-Wert-Paaren, die Umgebungsvariablen.

Auf Linux und Mac liest man Umgebungsvariablen mit folgendem Befehl in der Bash aus, z.B.:

echo $HOME

In Python liest man sie mit os.environ aus:

import os

os.environ["PATH"]  # Liest eine OS-Umgebungsvariable
os.environ["HOME"]

Wichtig zu verstehen: os.environ liest keine Dateien, also auch nicht die Datei .env. Es greift auf das zu, was das Betriebssystem dem Prozess beim Start mitgegeben hat. Eine .env-Datei allein reicht also nicht. Die Werte müssen zuerst in den Umgebunsvariablen-Pool gelangen.

Auf einem Server kann man das von Hand machen:

export DB_PASSWORD=supergeheim
export SECRET_KEY=abc123

Das ist mühsam und nicht dauerhaft. Genau hier setzt python-dotenv an.

Was macht python-dotenv?

python-dotenv ist ein kleines Package, das eine .env-Datei liest und die Werte in os.environ schreibt. Danach gibt es keinen Unterschied mehr zwischen einer Variable aus der .env-Datei und einer, die die Shell gesetzt hat.

from dotenv import load_dotenv
import os
load_dotenv("/pfad/zur/.env")  # Schreibt die Werte in os.environ
os.environ["SECRET_KEY"]  # Funktioniert jetzt

Das hat einen praktischen Vorteil: Auf Produktionsservern, die Umgebungsvariablen direkt über systemd, Docker oder ein Hosting-Panel setzen, kannst du die .env-Datei komplett weglassen. Der Django-Code bleibt in beiden Fällen identisch.

Kurzer Exkurs: .env in JavaScript

Der Begriff «.env-Datei» bedeutet je nach Umgebung etwas anderes. In Build-Tools wie Vite oder Next.js werden die Werte beim Build als String-Konstanten fest in den Code eingebaut; der Browser kennt keine OS-Umgebungsvariablen. In Node.js dagegen arbeitet dotenv gleich wie python-dotenv und schreibt die Werte in process.env. Gut zu wissen, wenn man zwischen den Welten wechselt.

Einrichten der Umgebungsvariablen Schritt für Schritt

Installation

Installiere python-dotenv:

pip install python-dotenv

Projektstruktur

Die .env-Datei gehört in den Projekt-Root, neben manage.py:

mein_projekt/
    manage.py
    .env           # die echten Werte, nie ins Git
    .env.example   # Vorlage ohne Werte, kommt ins Git
    .gitignore
    mein_projekt/
        settings.py

Die .env-Datei

# Datenbank
DB_ENGINE=django.db.backends.postgresql
DB_NAME=mein_projekt
DB_USER=mein_user
DB_PASSWORD=supergeheim
DB_HOST=localhost
DB_PORT=5432
# Django
SECRET_KEY=dein-django-secret-key
DEBUG=False
# Externe Services
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-...

settings.py

# mein_projekt/settings.py
from pathlib import Path
from dotenv import load_dotenv
import os

BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(BASE_DIR / ".env")  # Werte aus dem Projekt-Root laden

SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = os.environ.get("DEBUG", "False") == "True"
DATABASES = {
    "default": {
        "ENGINE": os.environ["DB_ENGINE"],
        "NAME": os.environ["DB_NAME"],
        "USER": os.environ["DB_USER"],
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ.get("DB_HOST", "localhost"),
        "PORT": os.environ.get("DB_PORT", "5432"),
    }
}
STRIPE_SECRET_KEY = os.environ["STRIPE_SECRET_KEY"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

.gitignore

Die echte .env-Datei darf nie ins Repository. Ein Eintrag in der Datei .gitignore genügt:

.env

.env.example

Diese Vorlage kommt ins Git. Sie zeigt neuen Entwicklern (oder dir selbst auf einem neuen Server), welche Variablen gesetzt sein müssen, ganz ohne echte Werte:

DB_ENGINE=django.db.backends.postgresql
DB_NAME=
DB_USER=
DB_PASSWORD=
DB_HOST=localhost
DB_PORT=5432
SECRET_KEY=
DEBUG=False
STRIPE_SECRET_KEY=

os.environ["KEY"] oder os.environ.get("KEY")?

Der Unterschied ist bewusst gewählt:

  • os.environ["KEY"] wirft sofort einen KeyError, wenn die Variable fehlt. Das ist bei Pflichtwerten wie SECRET_KEY oder DB_PASSWORD genau richtig. Der Fehler erscheint beim Start und nicht erst bei der ersten Datenbankverbindung mitten im Betrieb.
  • os.environ.get("KEY", "default") liefert einen Fallback. Sinnvoll für Werte mit brauchbarem Standard wie DB_HOST oder DB_PORT.

Als Faustregel gilt: Pflichtfelder eckig, optionale Werte mit .get() und Default.

Häufiger Fehler: load_dotenv ohne Pfad

load_dotenv()  # Sucht im aktuellen Arbeitsverzeichnis
load_dotenv(BASE_DIR / ".env")  # Sucht immer im Projekt-Root

load_dotenv() ohne Argument sucht die Datei im aktuellen Arbeitsverzeichnis des Prozesses. Wird Django aus einem anderen Verzeichnis gestartet, findet es die .env-Datei nicht, und zwar ohne Fehlermeldung. Die Variablen landen still gar nicht in os.environ, und du suchst den Fehler an der falschen Stelle. Mit dem expliziten Pfad BASE_DIR / ".env" passiert das nicht.

Was ist mit django-environ?

django-environ ist eine bekannte Alternative mit eingebautem Type-Casting und eigener API:

import environ
env = environ.Env()
env.read_env()
DEBUG = env.bool("DEBUG", default=False)
DATABASES = {"default": env.db("DATABASE_URL")}

Das ist bequem, bringt aber zwei Nachteile. Es ist Django-spezifisch und erwartet die Datenbank-Zugangsdaten oft als einzelnes DATABASE_URL-Format. Und es legt eine zusätzliche Abstraktionsschicht über os.environ, die man kennen muss. python-dotenv mit os.environ ist universeller und für jeden Python-Entwickler sofort lesbar, auch ohne die django-environ-API zu kennen.

Zusammenfassung

Die wichtigsten Punkte auf einen Blick:

  • Secrets gehören nicht in den Code, sondern in Umgebungsvariablen.
  • os.environ liest keine Dateien, sondern nur den Pool des Prozesses. python-dotenv füllt diesen Pool aus der .env-Datei.
  • Die .env-Datei liegt im Projekt-Root und gehört in .gitignore. Eine .env.example ohne Werte kommt ins Git.
  • Pflichtwerte mit os.environ["KEY"] lesen (Fehler beim Start), optionale mit os.environ.get("KEY", "default").
  • load_dotenv() immer mit explizitem Pfad BASE_DIR / ".env" aufrufen.
  • In der Produktion kannst du die .env-Datei weglassen und die Variablen direkt über systemd, Docker oder das Hosting-Panel setzen. Der Code bleibt gleich.

Handlungsempfehlung: Öffne dein aktuelles Projekt und prüfe settings.py auf fest gecodete Passwörter oder Keys. Zieh sie in eine .env-Datei, ergänze .gitignore und committe eine .env.example. Das sind zwanzig Minuten Arbeit und ein Sicherheitsrisiko weniger.

Quellen

python-dotenv auf PyPI (englisch)

python-dotenv auf GitHub (englisch)

Python-Dokumentation: os.environ (englisch)

Django-Dokumentation: DATABASES (englisch)

Django-Dokumentation: setting.py DATABASES (englisch)

Django-Dokumentation: SECRET_KEY (englisch)

django-environ Dokumentation (englisch)

Das könnte dich auch interessieren

Erstellen eines Django-Projektes

  • 30. Januar 2021

[Update 25.05.26] Ein Django-Projekt zu erstellen ist nicht schwer und schnell erledigt. Dennoch besteht die Installation aus mehreren Schritten. In diesem Blog-Post habe ich diese festgehalten. Auf diese Weise kann ich ein neues Projekt schnell aufsetzen und mit der Arbeit an der eigentlichen Idee beginnen.