TuBrief
구독 채널
비디오
커뮤니티

Technische Schritte zum Ausstieg aus Firebase und Wechsel zu 5-Dollar-Self-Hosting

TuBrief 편집팀
2026년 7월 14일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Deutsch한국어EnglishEspañol中文العربيةहिन्दीFrançaisPortuguêsРусскийBahasa Indonesia日本語

관련 영상

Diese kostenlose Go-Alternative zu Firebase besteht aus nur einer Datei7:49

Diese kostenlose Go-Alternative zu Firebase besteht aus nur einer Datei

Better Stack

커뮤니티의 다른 글

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

2026년 9월 13일

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

2026년 9월 13일

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

2026년 9월 13일

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

2026년 9월 13일

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Technische Schritte zum Ausstieg aus Firebase und Wechsel zu 5-Dollar-Self-Hosting

Firebase ist anfangs süß. Man muss sich keine Gedanken über die Infrastruktur machen und kann sich ganz auf die Implementierung von Funktionen konzentrieren. Doch sobald der Dienst auch nur ein wenig wächst, beginnen die Beträge auf der Rechnung zu steigen. Ein Wechsel zum Self-Hosting wirkt jedoch oft einschüchternd. Was, wenn die Daten verloren gehen? Was, wenn der Dienst bei jeder Bereitstellung stillsteht?

Das sind berechtigte Sorgen. Aber es gibt einen Weg. Ich habe den konkreten Prozess zusammengestellt, wie man Firestore-Daten sicher zu PocketBase – einem All-in-One-Backend auf SQLite-Basis – migriert und eine Umgebung für Zero-Downtime-Deployments mit GitHub Actions und Nginx aufbaut.


Pipeline zur Migration von Firestore-Daten zu PocketBase

Das erste Hindernis beim Übertragen unstrukturierter Daten aus Firestore in ein PocketBase-Schema auf Basis eines relationalen Modells ist die Länge der Identifikatoren. Firestore verwendet 20-stellige Dokument-IDs, während PocketBase standardmäßig eine Beschränkung auf 15 alphanumerische Zeichen hat. Ignoriert man diese Länge und überträgt die Daten einfach, erhält man den Fehler validation_length_invalid.

Um dieses Problem zu umgehen, muss die Länge unter Beibehaltung der Eindeutigkeit reduziert werden. Der sicherste Weg ist, die Firestore-ID mit SHA256 zu hashen und dann die ersten 15 Zeichen abzuschneiden. Sorgen um Kollisionen sind unbegründet: Selbst bei großen Datensätzen ist die Wahrscheinlichkeit, dass sich 15-stellige Hash-Werte überschneiden, so gering, dass sie vernachlässigt werden kann.

Die Struktur eines Node.js-Migrationsskripts, das dies verarbeitet, sieht wie folgt aus. Dabei werden die Daten mithilfe der Pakete firebase-admin und axios in 100er-Blöcken abgerufen.

`javascript
// migration.js
const admin = require('firebase-admin');
const axios = require('axios');
const crypto = require('crypto');

admin.initializeApp({
credential: admin.credential.applicationDefault()
});

const db = admin.firestore();
const PB_URL = 'http://127.0.0.1:8090/api/collections/posts/records';

async function migrate() {
let lastDoc = null;
let hasMore = true;

while (hasMore) {
let query = db.collection('posts').orderBy('name').limit(100);
if (lastDoc) {
query = query.startAfter(lastDoc);
}

const snapshot = await query.get();
if (snapshot.empty) {
  hasMore = false;
  break;
}

for (const doc of snapshot.docs) {
  const data = doc.data();
  // Konvertierung der 20-stelligen Firestore-ID in 15 alphanumerische Zeichen
  const newId = crypto.createHash('sha256').update(doc.id).digest('hex').substring(0, 15);
  
  try {
    await axios.post(PB_URL, {
      id: newId,
      firestore_id: doc.id, // Speichern der Original-ID zur Validierung
      title: data.title,
      content: data.content
    });
  } catch (err) {
    console.error(`Migration fehlgeschlagen: ${doc.id}`, err.response?.data);
  }
}
lastDoc = snapshot.docs[snapshot.docs.length - 1];

}
}

migrate();
`

Um diesen Vorgang ohne Unterbrechungen durchzuführen, sollte man eine Übergangsphase einplanen. Verschieben Sie zunächst über 90 % der bestehenden Daten im Hintergrund mit diesem Skript. Implementieren Sie dann in Ihrem Client-Code ein Dual-Write-Verfahren, bei dem Schreibvorgänge gleichzeitig sowohl an Firebase als auch an PocketBase gesendet werden. Sobald sichergestellt ist, dass die Daten auf beiden Seiten übereinstimmen, können Sie die Lese-Endpunkte auf PocketBase umstellen und den Firebase-Code entfernen. Für den Benutzer wird der Server nicht eine einzige Sekunde ausfallen.


Einrichtung eines 24-Stunden-stabilen PocketBase-Servers

PocketBase ist ein leichtgewichtiges Backend, das als einzelne Binärdatei ausgeführt wird. Wenn man es jedoch einfach nur auf einem Linux-Server startet, gibt es keine Möglichkeit, den Dienst wiederherzustellen, sollte der Prozess aufgrund eines unerwarteten Speichermangels (OOM) beendet werden.

Sie müssen systemd konfigurieren, damit der Prozess bei einem Absturz automatisch neu gestartet wird. Erstellen Sie eine Datei unter /etc/systemd/system/pocketbase.service und tragen Sie den folgenden Inhalt ein:

`ini
[Unit]
Description=PocketBase Service
After=network.target

[Service]
Type=simple
User=pocketbase
Group=pocketbase
LimitNOFILE=65535
ExecStart=/opt/pocketbase/pocketbase serve --http="127.0.0.1:8090"
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
`

Hier ist die Einstellung LimitNOFILE=65535 wichtig. Sie verhindert, dass bei hoher gleichzeitiger Nutzung der Echtzeit-Abonnements (WebSockets) die Verbindung aufgrund der Standard-Dateideskriptor-Grenzen von Linux unterbrochen wird.

An der Vorderseite verwenden wir Nginx für SSL-Zertifikate und als Proxy. Fügen Sie der Konfiguration /etc/nginx/sites-available/pocketbase Optionen hinzu, damit Echtzeit-Stream-Verbindungen nicht verzögert werden:

`nginx
upstream pocketbase {
server 127.0.0.1:8090;
}

server {
server_name api.yourdomain.com;

location / {
    proxy_pass http://pocketbase;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Unterstützung für WebSockets und SSE-Echtzeit-Streaming
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_buffering off;
}

}
`

Sobald Sie nun mit dem Befehl certbot --nginx ein HTTPS-Zertifikat hinzufügen, ist die grundlegende Infrastruktur fertig.

Erfahrungsgemäß können selbst die günstigsten VPS mit 512 MB RAM mehr als 1.000 leichte Anfragen pro Sekunde problemlos verarbeiten, wenn man die SQLite-Einstellungen leicht optimiert. Um die Leistung von SQLite beim Betrieb von PocketBase zu maximieren, sollte der WAL-Modus (Write-Ahead Log) aktiviert sein. PocketBase aktiviert diesen standardmäßig, aber falls Sie SQLite direkt bearbeiten, sollten Sie die Einstellungen PRAGMA journal_mode=WAL; und PRAGMA synchronous=NORMAL; überprüfen. Dadurch entfallen fast alle Engpässe, bei denen Anfragen warten müssen, während auf die Festplatte geschrieben wird.


Echtzeit-Backups mit Cloudflare R2 und Litestream

Der häufigste Fehler, den Indie-Hacker machen, ist das einfache Kopieren der SQLite-Datenbankdatei (data.db) von einem laufenden Server zu Sicherungszwecken. Dabei besteht ein hohes Risiko, dass die Daten während des Backups beschädigt werden.

Wir verwenden stattdessen Litestream. Es ist ein Tool, das die WAL-Frames von SQLite in Echtzeit abfängt und nur die geänderten Teile an den Cloud-Speicher sendet. Als Backup-Speicher empfehle ich Cloudflare R2, da hier keine Kosten für ausgehenden Datentransfer anfallen und Backups daher nahezu kostenlos sind.

Erstellen Sie eine Konfigurationsdatei unter /etc/litestream.yml wie folgt:

`yaml
dbs:

  • path: /opt/pocketbase/pb_data/data.db
    replicas:
    • type: s3
      bucket: your-r2-bucket-name
      endpoint: https://.r2.cloudflarestorage.com
      access-key-id:
      secret-access-key:

`

Mit dieser Einstellung können Sie selbst bei Verlust der ursprünglichen DB-Datei mit einem einzigen Befehl den Zustand bis auf die letzte Sekunde genau wiederherstellen:

bash litestream restore -if-replica-exists /opt/pocketbase/pb_data/data.db

Falls Sie während der Entwicklung versehentlich Daten löschen und zu einem bestimmten Zeitpunkt zurückkehren müssen, ist auch das möglich:

`bash

1. PocketBase anhalten

sudo systemctl stop pocketbase.service

2. Wiederherstellungsdatei für einen bestimmten Zeitpunkt erstellen

litestream restore -timestamp "2026-07-14T15:00:00Z" -o /tmp/recovered.db /opt/pocketbase/pb_data/data.db

3. Datenintegrität prüfen und Datei ersetzen

sqlite3 /tmp/recovered.db "PRAGMA integrity_check;"
mv /tmp/recovered.db /opt/pocketbase/pb_data/data.db
chown -R pocketbase:pocketbase /opt/pocketbase/pb_data/

4. Dienst fortsetzen

sudo systemctl start pocketbase.service
`

Cloudflare R2 bietet monatlich 10 GB kostenlosen Speicherplatz. Dazu kommen kostenlose Kontingente für 1 Million Schreib- und 10 Millionen Lesezugriffe, sodass die Backup-Kosten für einen Einzel-Dienst nahezu bei Null liegen.


Zero-Downtime-Deployment mit GitHub Actions und Nginx

Es ist bequem, mit Docker zu deployen, aber auf kleinen VPS mit 512 MB oder 1 GB RAM ist der Speicher, den der Docker-Daemon selbst verbraucht, bereits kostbar. Wir implementieren ein Zero-Downtime-Deployment ohne Docker durch das wechselnde Umschalten von Ports im Linux systemd (Blue-Green Deployment).

Wir nutzen die Eigenschaft von SQLite, dass mehrere Prozesse eine einzelne Datenbankdatei lesen und schreiben können. Wir registrieren eine systemd-Dienstvorlage, mit der die PocketBase-Binärdatei jeweils auf Port 9011 (Blau) und Port 9012 (Grün) gestartet werden kann.

Hier ist ein Beispiel für einen Workflow, der eine mit GitHub Actions erstellte leichtgewichtige Binärdatei auf den Server überträgt:

`yaml

.github/workflows/deploy.yml

name: Deploy
on:
push:
branches: [ main ]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o pocketbase main.go
- name: Transfer Binary to VPS
uses: appleboy/scp-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_KEY }}
source: "pocketbase"
target: "/srv/pocketbase/next_release"
`

Sobald die Binärdatei auf dem Server ankommt, wird das Deployment-Skript ausgeführt. Es prüft, ob derzeit Port 9011 oder 9012 aktiv ist, und startet die neue Version der Binärdatei auf dem freien Port:

`bash
#!/bin/bash

deploy_swap.sh

CURRENT_PORT=$(curl -s http://127.0.0.1:8090/api/health | jq -r '.port' 2>/dev/null || echo "9011")

if [ "$CURRENT_PORT" = "9011" ]; then
TARGET_PORT="9012"
else
TARGET_PORT="9011"
fi

Neue Binärdatei auf dem Ziel-Port im Hintergrund starten

sudo systemctl start pocketbase@$TARGET_PORT.service

3 Sekunden warten für Health Check

sleep 3
HEALTH_CHECK=(curl−shttp://127.0.0.1:(curl -s http://127.0.0.1:(curl−shttp://127.0.0.1:TARGET_PORT/api/health | jq -r '.status')

if [ "HEALTH_CHECK" = "OK" ]; then # Nginx Upstream-Einstellung auf den neuen Port anpassen und neu laden echo "upstream pocketbase { server 127.0.0.1:TARGET_PORT; }" | sudo tee /etc/nginx/conf.d/upstream.conf
sudo systemctl reload nginx

# Prozess der alten Version beenden

sudo systemctl stop pocketbase@$CURRENT_PORT.service
echo "Deployment abgeschlossen. Umschaltung auf Port TARGETPORTerfolgreich."elseecho"Deploymentfehlgeschlagen.HealthCheckfu¨rneueVersionnichterfolgreich."sudosystemctlstoppocketbase@TARGET_PORT erfolgreich." else echo "Deployment fehlgeschlagen. Health Check für neue Version nicht erfolgreich." sudo systemctl stop pocketbase@TARGETP​ORTerfolgreich."elseecho"Deploymentfehlgeschlagen.HealthCheckfu¨rneueVersionnichterfolgreich."sudosystemctlstoppocketbase@TARGET_PORT.service
exit 1
fi
`

Mit dieser Methode funktioniert ein Zero-Downtime-Deployment perfekt, ohne auf teure Container-Orchestrierungstools angewiesen zu sein. Selbst wenn ein Benutzer während des Deployments eine Anfrage sendet, hält Nginx diese für den Bruchteil einer Sekunde (im Millisekundenbereich) während des Neuladens an und leitet sie dann sicher an den neuen Port weiter.

Self-Hosting erfordert bei der ersten Einrichtung zwar etwas Arbeit, aber sobald die Konfiguration steht, ist es eine hervorragende Grundlage, auf der man sich ohne Sorgen um monatlich anfallende Infrastrukturkosten voll und ganz auf das Produkt konzentrieren kann.