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 einenKeyError, wenn die Variable fehlt. Das ist bei Pflichtwerten wieSECRET_KEYoderDB_PASSWORDgenau 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 wieDB_HOSToderDB_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.environliest keine Dateien, sondern nur den Pool des Prozesses.python-dotenvfüllt diesen Pool aus der.env-Datei.- Die .
env-Datei liegt im Projekt-Root und gehört in.gitignore. Eine.env.exampleohne Werte kommt ins Git. - Pflichtwerte mit
os.environ["KEY"]lesen (Fehler beim Start), optionale mitos.environ.get("KEY", "default"). load_dotenv()immer mit explizitem PfadBASE_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.