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

Pasos técnicos para abandonar Firebase y cambiar a un alojamiento propio por 5 dólares al mes

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

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

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

관련 영상

Esta alternativa gratuita a Firebase en Go es solo un archivo7:49

Esta alternativa gratuita a Firebase en Go es solo un archivo

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
구독 채널
비디오
커뮤니티
로그인

Pasos técnicos para abandonar Firebase y cambiar a un alojamiento propio por 5 dólares al mes

Firebase es dulce al principio. Puedes implementar funciones sin preocuparte por la infraestructura. Pero incluso cuando el servicio crece apenas un poco, las cifras de la factura comienzan a aumentar. Aun así, la idea de migrar a un alojamiento propio da miedo. ¿Qué pasa si se pierden los datos? ¿Qué pasa si el servicio se detiene cada vez que realizo una implementación?

Es una preocupación comprensible. Pero hay una manera de hacerlo. He recopilado un método concreto para migrar de forma segura los datos de Firestore a PocketBase, un backend todo en uno basado en SQLite, y crear un entorno de despliegue sin tiempo de inactividad utilizando GitHub Actions y Nginx.


Pipeline para migrar datos de Firestore a PocketBase

El primer obstáculo al migrar datos no estructurados de Firestore al esquema de PocketBase, basado en un modelo relacional, es la longitud del identificador. Firestore utiliza ID de documento de 20 caracteres, pero PocketBase impone por defecto una restricción alfanumérica de 15 caracteres. Si ignoras esta longitud e intentas forzar la migración, te encontrarás con el error validation_length_invalid.

Para evitar este problema, debes reducir la longitud manteniendo la unicidad. La forma más segura es aplicar hashing SHA256 al ID de Firestore y luego truncar el resultado a los primeros 15 caracteres. Puedes olvidarte de preocuparte por las colisiones. Incluso en conjuntos de datos a gran escala, la probabilidad de que los valores hash de 15 caracteres se superpongan es tan baja que puede ignorarse.

La estructura del script de migración en Node.js para manejar esto es la siguiente. Utiliza los paquetes firebase-admin y axios para recuperar los datos en lotes de 100.

`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();
  // Convertir el ID de Firestore de 20 caracteres a alfanumérico de 15 caracteres
  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, // Guardar el ID original por si acaso para validación
      title: data.title,
      content: data.content
    });
  } catch (err) {
    console.error(`Error en la transferencia: ${doc.id}`, err.response?.data);
  }
}
lastDoc = snapshot.docs[snapshot.docs.length - 1];

}
}

migrate();
`

Para hacer esto sin tiempo de inactividad, debes establecer un periodo de transición. Primero, migra más del 90% de los datos existentes en segundo plano con este script. Luego, implementa un código de "escritura dual" (Dual-Write) en el código del cliente que escriba tanto en Firebase como en PocketBase simultáneamente cuando se realicen operaciones de escritura. Una vez confirmado que los datos en ambos lados coinciden, cambia los endpoints de lectura a PocketBase y elimina el código de Firebase. Desde la perspectiva del usuario, el servidor no se detendrá ni por un segundo.


Configuración de un servidor PocketBase que funciona las 24 horas

PocketBase es un backend ligero que se ejecuta como un binario único. Sin embargo, si simplemente lo ejecutas en un servidor Linux sin más, no hay forma de recuperar el servicio si el proceso se cierra debido a una falta de memoria inesperada (OOM).

Debes configurar systemd para que el proceso se reinicie solo si muere. Crea el archivo /etc/systemd/system/pocketbase.service y registra el siguiente contenido:

`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
`

Aquí, la configuración LimitNOFILE=65535 es importante. Cuando utilizas la función de suscripción en tiempo real (WebSockets), que es un punto fuerte de PocketBase, evita que las conexiones se corten debido a que se alcanza el límite de descriptores de archivo predeterminado de Linux cuando hay muchos usuarios conectados simultáneamente.

Coloca Nginx al frente para manejar el certificado SSL y el proxy. Debes incluir opciones en la configuración de /etc/nginx/sites-available/pocketbase para evitar retrasos en las conexiones de streaming en tiempo real.

`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;

    # Soporte para WebSockets y streaming en tiempo real SSE
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_buffering off;
}

}
`

Ahora, con el comando certbot --nginx, solo necesitas añadir el certificado HTTPS y la configuración básica de la infraestructura estará terminada.

Según mi experiencia, incluso en el nivel de VPS más barato con 512 MB de RAM, con solo ajustar un poco la configuración de SQLite, puede manejar fácilmente más de 1,000 peticiones ligeras por segundo. Para maximizar el rendimiento de SQLite al ejecutar PocketBase, debes activar el modo WAL (Write-Ahead Log). PocketBase activa el modo WAL por defecto, pero si necesitas trabajar directamente con SQLite, asegúrate de comprobar los ajustes PRAGMA journal_mode=WAL; y PRAGMA synchronous=NORMAL;. El cuello de botella en el que las peticiones esperan a que se escriba en el disco casi desaparecerá.


Copias de seguridad en tiempo real con Cloudflare R2 y Litestream

El error más común que cometen los desarrolladores independientes es copiar el archivo de base de datos SQLite (data.db) directamente desde el servidor en funcionamiento para realizar una copia de seguridad. Este método tiene un alto riesgo de corromper los datos durante la copia.

Utilizamos Litestream. Es una herramienta que intercepta los marcos WAL de SQLite en tiempo real y envía solo las partes modificadas al almacenamiento en la nube. Recomiendo Cloudflare R2 como almacenamiento de respaldo. Dado que las tarifas de transferencia de salida son gratuitas, casi no gastarás dinero por muchas copias de seguridad que hagas.

Escribe el archivo de configuración /etc/litestream.yml de la siguiente manera:

`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:

`

Si configuras esto, aunque se pierda el archivo db original, puedes restaurarlo al estado exacto de hace menos de un segundo con una sola línea de comando:

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

Si accidentalmente borras datos durante el desarrollo y quieres restaurarlos a un punto específico en el tiempo, también es posible especificar el valor de tiempo:

`bash

1. Pausar PocketBase

sudo systemctl stop pocketbase.service

2. Crear archivo de restauración a un momento específico

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

3. Verificar integridad de datos y reemplazar archivo

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. Reanudar servicio

sudo systemctl start pocketbase.service
`

Cloudflare R2 ofrece 10 GB de almacenamiento gratuito al mes. Además, incluye créditos gratuitos para 1 millón de peticiones de escritura y 10 millones de peticiones de lectura, por lo que, a escala de un servicio para una sola persona, el coste de las copias de seguridad se acerca a 0.


Despliegue sin tiempo de inactividad utilizando GitHub Actions y Nginx

Es cómodo realizar despliegues ejecutando Docker, pero en un VPS pequeño con 512 MB o 1 GB de RAM, la memoria que consume el propio demonio de Docker es un recurso valioso. Implementemos un despliegue sin tiempo de inactividad (blue-green) alternando puertos en systemd de Linux sin usar Docker.

Aprovechamos la característica de SQLite de que múltiples procesos pueden leer y escribir en un mismo archivo. Puedes registrar plantillas de servicio de systemd para que el binario de PocketBase se ejecute en el puerto 9011 (azul) y 9012 (verde) respectivamente.

Primero, un ejemplo de flujo de trabajo (workflow) para enviar el binario ligero construido con GitHub Actions al servidor:

`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"
`

Cuando el binario llega al servidor, se ejecuta el script de despliegue. Comprueba si el puerto activo actual es 9011 o 9012, y ejecuta la nueva versión del binario en el puerto que está libre.

`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

Ejecutar el nuevo binario en segundo plano en el puerto objetivo

sudo systemctl start pocketbase@$TARGET_PORT.service

Esperar 3 segundos para la comprobación de salud

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 # Modificar y recargar la configuración upstream de Nginx al nuevo puerto echo "upstream pocketbase { server 127.0.0.1:TARGET_PORT; }" | sudo tee /etc/nginx/conf.d/upstream.conf
sudo systemctl reload nginx

# Detener el proceso de la versión anterior
sudo systemctl stop pocketbase@$CURRENT_PORT.service
echo "Despliegue completado. Cambio al puerto $TARGET_PORT realizado con éxito."

else
echo "Error en el despliegue. Fallo en la comprobación de salud de la nueva versión."
sudo systemctl stop pocketbase@$TARGET_PORT.service
exit 1
fi
`

Al utilizar este método, el despliegue sin tiempo de inactividad funciona perfectamente sin necesidad de costosas herramientas de orquestación de contenedores. Incluso si un usuario envía una petición durante el despliegue, Nginx espera un instante (en milisegundos) mientras se recarga y transfiere de forma segura la petición al nuevo puerto.

El alojamiento propio solo requiere esfuerzo en la configuración inicial, pero una vez establecida, se convierte en una excelente base que te permite concentrarte exclusivamente en el producto, sin preocuparte por los costes de infraestructura que se cargan mensualmente.