Wie erstellt man eine eigene WebApp mit REST-API auf Basis von Python und betreibt sie in einem Docker-Container?
REST-API ist eine Benutzer-Schnittstelle, die den Designprinzipien der REST-Architektur entspricht (das soll an dieser Stelle reichen). FastAPI ist ein Framework für Python welches die Entwicklung von REST-APIs ermöglicht. In dieser Anleitung richten wir auf Debian 13 mit Xfce einen vollständigen FastAPI-Entwicklungsserver ein. Wir verwenden Docker Compose, bearbeiten unseren Python-Quellcode mit einem grafischen Editor und testen die fertige API direkt im Browser.
Das Besondere: Änderungen am Programm werden automatisch erkannt. Wir müssen den Docker-Container nicht bei jeder Änderung neu erstellen oder starten.
Die Anleitung eignet sich besonders für Einsteiger, die ihre erste Webanwendung mit Python und Docker entwickeln möchten. Der Bezug zu KI-Anwendungen liegt in der Tatsache, dass diese Anleitung durch KI erstellt wurde und vom Blog-Author geprüft und getestet wurde.
1. Was wir aufbauen
Unser Ziel ist eine kleine Python-Webanwendung, die innerhalb eines Docker-Containers läuft.
Die Anwendung stellt drei API-Endpunkte bereit:
/– Gibt eine Begrüßung und den Serverstatus zurück./zeit– Liefert die aktuelle Serverzeit./berechnen– Addiert zwei Zahlen und gibt das Ergebnis zurück.
Wir können diese Funktionen direkt über unseren Browser aufrufen.
Die Entwicklungsumgebung besteht aus folgenden Komponenten:
| Komponente | Aufgabe |
|---|---|
| Debian 13 | Betriebssystem |
| Xfce | Grafische Desktopoberfläche |
| Docker Engine | Ausführung der Container |
| Docker Compose | Konfiguration und Verwaltung der Anwendung |
| Python 3.13 | Programmiersprache |
| FastAPI | Framework zur Erstellung unserer API |
| Uvicorn | Webserver für FastAPI |
| gedit | Grafischer Quellcode-Editor |
| Firefox | Testen der API im Browser |
Die gesamte Anwendung befindet sich in unserem Projektverzeichnis:
~/Projekte/docker-test/
│
├── app.py
├── requirements.txt
├── Dockerfile
└── compose.yaml
Jede dieser Dateien erfüllt eine bestimmte Aufgabe.
2. Voraussetzungen
Bevor wir beginnen, sollten folgende Voraussetzungen erfüllt sein:
- Debian 13 ist installiert.
- Eine grafische Desktopumgebung wie Xfce ist vorhanden.
- Docker Engine funktioniert.
- Docker Compose ist installiert.
- Der Benutzer darf Docker-Befehle ausführen.
- Ein grafischer Texteditor wie gedit ist vorhanden.
- Eine Internetverbindung besteht, damit Docker benötigte Images und Python-Pakete herunterladen kann.
Zur Debian/Docker Installation gibt es 2 Artikel im Grundlagen-Bereich:
- Eigene Entwicklungsumgebung für WebApps auf einem Linux-Server einrichten – Teil 1 – Linux und Docker installieren
- Eigene Entwicklungsumgebung für WebApps auf einem Linux-Server einrichten – Teil 2 – Erstes Docker-basiertes Projekt
3. Projektverzeichnis erstellen
Zunächst öffnen wir ein Terminal und wechseln in unser Projektverzeichnis.
mkdir -p ~/Projekte/docker-test
cd ~/Projekte/docker-test
Alle weiteren Dateien legen wir in diesem Verzeichnis ab.
4. Unsere erste FastAPI-Anwendung programmieren
Wir erstellen zunächst die eigentliche Python-Anwendung.
Dazu öffnen wir den grafischen Texteditor:
gedit app.py
Wir fügen folgenden Python-Code ein:
from fastapi import FastAPI
from datetime import datetime
app = FastAPI()
@app.get("/")
def startseite():
return {
"nachricht": "Hallo von meinem FastAPI-Webserver!",
"status": "Docker funktioniert"
}
@app.get("/zeit")
def aktuelle_zeit():
return {
"zeit": datetime.now().isoformat()
}
@app.get("/berechnen")
def berechnen(a: float, b: float):
return {
"a": a,
"b": b,
"ergebnis": a + b
}
Anschließend speichern wir die Datei als app.py.
Wie funktioniert dieser Code?
Zunächst importieren wir die benötigten Komponenten:
from fastapi import FastAPI
from datetime import datetime
Mit
app = FastAPI()
erzeugen wir unsere FastAPI-Anwendung. Sie wird komplett durch das verwendete fastAPI Framework erzeugt.
Danach definieren wir drei sogenannte Endpunkte.
Endpunkt 1: Die Startseite
@app.get("/")
def startseite():
return {
"nachricht": "Hallo von meinem FastAPI-Webserver!",
"status": "Docker funktioniert"
}
Dieser Endpunkt reagiert auf HTTP-GET-Anfragen an die Startadresse /.
FastAPI wandelt das zurückgegebene Python-Dictionary (die Text-Struktur in den geschweiften Klammern) automatisch in eine JSON-Antwort um.
Endpunkt 2: Die aktuelle Uhrzeit
@app.get("/zeit")
def aktuelle_zeit():
return {
"zeit": datetime.now().isoformat()
}
Dieser Endpunkt gibt die aktuelle Zeit des Servers zurück.
Die Zeit stammt aus der Laufzeitumgebung des Containers. Die verwendete Zeitzone hängt von dessen Konfiguration ab.
Endpunkt 3: Zwei Zahlen addieren
@app.get("/berechnen")
def berechnen(a: float, b: float):
return {
"a": a,
"b": b,
"ergebnis": a + b
}
Hier erwartet FastAPI zwei Parameter: a und b. Beide werden als Gleitkommazahlen interpretiert.
Die Funktion addiert diese Werte und gibt das Ergebnis als JSON zurück.
Damit haben wir bereits eine kleine, funktionsfähige REST-API programmiert.
5. Python-Abhängigkeiten definieren
Unsere Anwendung verwendet FastAPI und Uvicorn.
Diese Pakete sollen nicht direkt auf Debian installiert werden, sondern ausschließlich innerhalb unseres Docker-Images.
Dafür erstellen wir eine Datei namens requirements.txt.
gedit requirements.txt
Inhalt:
fastapi
uvicorn[standard]
FastAPI ist das Python-Framework, mit dem wir unsere API-Endpunkte erstellen.
Uvicorn ist ein ASGI-Webserver, der unsere FastAPI-Anwendung ausführt und HTTP-Anfragen entgegennimmt.
Die Option [standard] installiert zusätzliche, für Uvicorn nützliche Abhängigkeiten.
Für dieses Lernprojekt verwenden wir die Paketnamen ohne feste Versionsnummern. Bei einem späteren produktiven Projekt sollten wir die Versionen festlegen, damit unsere Anwendung reproduzierbar gebaut werden kann.
6. Das Dockerfile erstellen
Nun benötigt Docker eine Bauanleitung für unser Image.
Diese Bauanleitung heißt Dockerfile.
Wir erstellen die Datei:
gedit Dockerfile
Inhalt:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Speichern nicht vergessen.
Was bedeuten die einzelnen Dockerfile-Anweisungen?
FROM – Basis-Image auswählen
FROM python:3.13-slim
Wir verwenden ein schlankes Python-3.13-Image.
Dieses enthält bereits Python und die grundlegenden Komponenten, die wir für unsere Anwendung benötigen.
WORKDIR – Arbeitsverzeichnis festlegen
WORKDIR /app
Innerhalb des Containers verwenden wir /app als Arbeitsverzeichnis.
COPY – Abhängigkeiten übernehmen
COPY requirements.txt .
Damit kopieren wir unsere Datei requirements.txt in das Image.
RUN – Python-Pakete installieren
RUN pip install --no-cache-dir -r requirements.txt
Docker installiert beim Image-Bau FastAPI und Uvicorn.
Die Option --no-cache-dir verhindert, dass pip seinen Download-Cache im Image speichert.
EXPOSE – Anwendungsport dokumentieren
EXPOSE 8000
Unsere Anwendung verwendet Port 8000.
Wichtig: EXPOSE veröffentlicht diesen Port noch nicht automatisch auf unserem Debian-Rechner. Das konfigurieren wir anschließend mit Docker Compose.
CMD – Webserver starten
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Beim Start des Containers führt Docker Uvicorn aus.
Die einzelnen Parameter haben folgende Bedeutung:
| Parameter | Bedeutung |
|---|---|
app:app | Python-Modul app.py und darin enthaltenes FastAPI-Objekt app |
--host 0.0.0.0 | Uvicorn lauscht auf allen Netzwerkschnittstellen innerhalb des Containers |
--port 8000 | Webserver verwendet Port 8000 |
--reload | Anwendung bei Quellcodeänderungen automatisch neu laden |
Die Option --reload ist ausdrücklich für unsere Entwicklungsumgebung gedacht.
In einer späteren Produktionsumgebung würden wir den Webserver ohne diese Option betreiben.
7. Docker Compose konfigurieren
Bisher haben wir ein Python-Programm und eine Bauanleitung für das Docker-Image.
Jetzt benötigen wir eine Konfiguration, mit der wir unseren Container bequem starten und verwalten können.
Dafür verwenden wir Docker Compose.
Wir erstellen:
gedit compose.yaml
Inhalt:
services:
app:
build: .
container_name: fastapi-dev
ports:
- "127.0.0.1:8000:8000"
volumes:
- ./:/app:ro
environment:
- PYTHONUNBUFFERED=1
Die einzelnen Einstellungen erklärt
services
services:
app:
Wir definieren einen Dienst mit dem Namen app.
Später könnten wir hier weitere Dienste ergänzen, beispielsweise PostgreSQL oder ein Frontend.
build
build: .
Docker soll das Image anhand des Dockerfiles im aktuellen Projektverzeichnis erstellen.
container_name
container_name: fastapi-dev
Unser Container erhält den Namen fastapi-dev.
ports
ports:
- "127.0.0.1:8000:8000"
Damit verbinden wir Port 8000 des Containers mit Port 8000 auf unserem Debian-Rechner.
Die allgemeine Schreibweise lautet:
HOST-IP:HOST-PORT:CONTAINER-PORT
Durch die Angabe 127.0.0.1 beschränken wir den Zugriff auf den lokalen Rechner.
Unsere Entwicklungs-API ist dadurch nicht unmittelbar aus dem Heimnetz oder Internet erreichbar.
Das ist für die ersten Tests eine sinnvolle Sicherheitseinstellung.
volumes
volumes:
- ./:/app:ro
Diese Einstellung ist für unsere Entwicklungsumgebung besonders interessant.
Sie verbindet den gesamten Projekt-Ordner (docker-test) auf unserem Debian-Rechner mit dem Ordner /app im Container.
Das bedeutet:
Wir bearbeiten unseren Quellcode (der durchaus aus mehreren Dateien bestehen kann) weiterhin mit gedit auf Debian. Der Container verwendet alle Quell-Dateien die dem Ordner /app zugewiesen wurden..
Die Option ro steht für read-only. Der Container darf die eingebundene Datei lesen, aber nicht verändern.
environment
environment:
- PYTHONUNBUFFERED=1
Diese Einstellung sorgt dafür, dass Python seine Ausgaben ohne die übliche Pufferung an die Container-Ausgabe weitergibt.
Das ist bei der Fehlersuche und beim Anzeigen von Logs hilfreich.
8. Docker-Container erstellen und starten
Jetzt sind alle benötigten Dateien vorhanden.
Unsere Projektstruktur sieht folgendermaßen aus:
~/Projekte/docker-test/
│
├── app.py
├── requirements.txt
├── Dockerfile
└── compose.yaml
Im Terminal wechseln wir in das Projektverzeichnis:
cd ~/Projekte/docker-test
Anschließend starten wir:
docker compose up --build -d
Was passiert bei diesem Befehl?
Docker Compose liest zunächst unsere compose.yaml.
Anschließend wird das Dockerfile verarbeitet.
Falls das Python-Basis-Image noch nicht vorhanden ist, lädt Docker es herunter.
Danach installiert Docker die benötigten Python-Pakete und erstellt unser eigenes Image.
Schließlich wird der Container erzeugt und gestartet.
Die Parameter bedeuten:
| Parameter | Funktion |
|---|---|
up | Dienste erstellen und starten |
--build | Image vor dem Start neu bauen |
-d | Container im Hintergrund ausführen |
Nach dem Start überprüfen wir den Status:
docker compose ps
Unser Container fastapi-dev sollte als laufend angezeigt werden.
Falls Probleme auftreten, können wir die Ausgaben des Webservers ansehen:
docker compose logs -f
Mit Strg+C verlassen wir die Log-Anzeige. Der im Hintergrund laufende Container bleibt dabei aktiv.
9. FastAPI im Browser testen
Jetzt kommt der spannende Moment.
Wir öffnen Firefox auf unserem Debian-Desktop und geben folgende Adresse ein:
Wenn alles funktioniert, erscheint eine JSON-Antwort:
{
"nachricht": "Hallo von meinem FastAPI-Webserver!",
"status": "Docker funktioniert"
}
Damit läuft unser erster eigener FastAPI-Webserver erfolgreich innerhalb eines Docker-Containers.
Die aktuelle Serverzeit abfragen
Als Nächstes öffnen wir:
Die Antwort sieht beispielsweise so aus:
{
"zeit": "2026-10-28T11:30:00.123456"
}
Der genaue Wert hängt natürlich vom Zeitpunkt der Anfrage ab.
Eine Berechnung durchführen
Jetzt testen wir unseren dritten Endpunkt:
http://localhost:8000/berechnen?a=25&b=17
FastAPI übernimmt die beiden Zahlen aus der URL.
Die Antwort lautet:
{
"a": 25.0,
"b": 17.0,
"ergebnis": 42.0
}
Damit haben wir bereits eine kleine API, die Parameter entgegennimmt, eine Berechnung durchführt und das Ergebnis als JSON zurückliefert.
10. Änderungen am Quellcode automatisch übernehmen
Ein wesentlicher Vorteil unserer Entwicklungsumgebung ist die direkte Verbindung zwischen dem Quellcode auf Debian und dem laufenden Container.
Wir öffnen die Datei erneut:
gedit ~/Projekte/docker-test/app.py
Dort suchen wir folgende Zeile:
"nachricht": "Hallo von meinem FastAPI-Webserver!"
Wir ändern sie in:
"nachricht": "Meine erste eigene Web-App läuft!"
Anschließend speichern wir die Datei.
Jetzt aktualisieren wir die Startseite im Browser:
Die neue Nachricht sollte bereits angezeigt werden.
Warum funktioniert das ohne einen neuen Docker-Build?
Das liegt an zwei Einstellungen.
Erstens haben wir unseren gesamten Projekt-Ordner als Bind-Mount eingebunden:
volumes:
- ./:/app:ro
Dadurch verwendet der Container die Datei direkt aus unserem Projektverzeichnis.
Zweitens haben wir Uvicorn mit --reload gestartet.
Uvicorn überwacht den Quellcode auf Änderungen und lädt die Anwendung bei Bedarf automatisch neu. Als Quellcode gelten zunächst einmal nur die Dateien mit der Endung .py
Änderungen an der Compose.yaml oder requirements.txt führen nicht zu einem Rebuild und einem Neustart des Containers.
Unser Entwicklungsablauf sieht deshalb so aus:
Grafischer Editor
│
▼
app.py bearbeiten
│
▼
Datei speichern
│
▼
Docker-Bind-Mount
│
▼
Uvicorn erkennt Änderung
│
▼
FastAPI wird neu geladen
│
▼
Browser aktualisieren
Wichtig: Änderungen an requirements.txt oder am Dockerfile erfordern weiterhin einen neuen Image-Build. Das automatische Neuladen betrifft ausschliesslich unseren eingebundenen Python-Quellcode.
11. Die interaktive FastAPI-Dokumentation kennenlernen
FastAPI besitzt eine besonders praktische Funktion: Es erzeugt automatisch eine interaktive API-Dokumentation.
Diese erreichen wir über:
Dort werden unsere drei Endpunkte angezeigt:
GET /
GET /zeit
GET /berechnen
Wir können jeden Endpunkt aufklappen und direkt ausprobieren.
Bei /berechnen lassen sich beispielsweise die Werte für a und b eingeben.
Anschließend klicken wir auf Execute.
FastAPI zeigt die erzeugte Anfrage und die Antwort des Servers an.
Für die Entwicklung komplexerer Anwendungen ist diese Oberfläche ausgesprochen hilfreich.
Sie ermöglicht es uns, Backend-Funktionen zu testen, bevor überhaupt eine eigene grafische Benutzeroberfläche existiert.
12. Container verwalten
Zum Abschluss schauen wir uns einige nützliche Docker-Compose-Befehle an.
Laufende Container anzeigen:
docker compose ps
Logs anzeigen:
docker compose logs -f
Container stoppen:
docker compose stop
Gestoppten Container erneut starten:
docker compose start
Container entfernen:
docker compose down
Image neu bauen und Container starten:
docker compose up --build -d
Bei docker compose down werden die von Compose verwalteten Container und Netzwerke entfernt.
Unsere Projektdateien auf Debian bleiben erhalten.
13. Warum Docker für die Entwicklung sinnvoll ist
Wir hätten FastAPI und Uvicorn auch direkt auf Debian installieren können.
Mit Docker erhalten wir jedoch eine sauber abgegrenzte Laufzeitumgebung.
Unser Debian-System benötigt selbst keine separate Installation von Python 3.13, FastAPI oder Uvicorn für dieses Projekt.
Diese Komponenten befinden sich im Docker-Image.
Das bringt mehrere Vorteile:
Saubere Trennung: Die Abhängigkeiten unserer Webanwendung sind von den Anwendungen auf dem Debian-Desktop getrennt.
Reproduzierbarkeit: Wir können unsere Entwicklungsumgebung anhand von Dockerfile und Compose-Konfiguration erneut erstellen.
Portabilität: Die Anwendung lässt sich später grundsätzlich auch auf einem anderen Linux-Rechner mit Docker betreiben.
Einfachere Erweiterung: Wir können weitere Container hinzufügen, ohne alle Komponenten direkt auf Debian installieren zu müssen.
Komfortable Entwicklung: Der Quellcode bleibt auf unserer SSD und kann mit einem grafischen Editor bearbeitet werden.
Für die spätere Bereitstellung auf einem anderen Rechner müssen wir gegebenenfalls Pfade, Netzwerkeinstellungen und Konfigurationen anpassen.
14. Wie geht es weiter?
Mit unserer ersten FastAPI-Anwendung haben wir die Grundlage für eine umfangreichere Web-App geschaffen.
Als nächste Ausbaustufen bieten sich an:
- PostgreSQL integrieren: Unsere Anwendung erhält eine eigene Datenbank, in der wir Informationen dauerhaft speichern können.
- Ein Frontend erstellen: Statt ausschließlich JSON-Antworten darzustellen, entwickeln wir eine grafische Benutzeroberfläche.
- Entwicklung und Produktion trennen: Wir richten getrennte Konfigurationen und Datenbanken für Tests und den produktiven Betrieb ein.
- C/C++-Berechnungen einbinden: Bei Bedarf ergänzen wir unsere Python-Anwendung um rechenintensive Funktionen.
- HTTPS mit Caddy einrichten: Später kann unsere Web-App über eine eigene Domain sicher erreichbar gemacht werden.
Für einen öffentlich erreichbaren Produktivserver sind außerdem Sicherheitsmaßnahmen erforderlich. Dazu gehören unter anderem der Verzicht auf den Entwicklungsmodus --reload, eine geeignete HTTPS-Konfiguration, regelmäßige Updates und eine durchdachte Verwaltung von Zugangsdaten.
Fazit
Mit Debian 13, Docker Compose, Python und FastAPI lässt sich bereits mit wenigen Dateien ein funktionsfähiger Webserver erstellen.
Wir haben gelernt, wie wir ein Docker-Image bauen, einen Container starten, Ports konfigurieren und Quellcode vom Hostsystem einbinden.
Besonders praktisch ist die Kombination aus einer grafischen Linux-Arbeitsumgebung und Docker: Wir bearbeiten unsere Programme bequem mit gedit, während die benötigten Laufzeitkomponenten sauber innerhalb der Container organisiert bleiben.
Unsere erste FastAPI-Anwendung kann bereits HTTP-Anfragen verarbeiten, die aktuelle Serverzeit zurückgeben und einfache Berechnungen durchführen.
Damit ist der Grundstein für eine eigene Full-Stack-Webanwendung gelegt.
Der nächste Teil dieser Artikel-Serie wird sich mit einer Architektur-Frage zur WebApp beschäftigen, also dranbleiben.