bolt Valebyte VPS desde $4/mes — NVMe, despliegue en 60s.

Obtener VPS arrow_forward
eco Principiante Tutorial/Cómo hacer

Despliegue de Plane en un VPS: gestión de proyectos self-hosted, SSL y copias de seguridad

calendar_month Oct 04, 2026 schedule 19 min de lectura visibility 85 vistas
Развёртывание Plane на VPS: self-hosted управление проектами, SSL и резервные копии
info

¿Necesitas un servidor para esta guía? Ofrecemos servidores dedicados y VPS en más de 50 países con configuración instantánea.

¿Necesitas un VPS para esta guía?

Explore otras opciones de servidores dedicados en

Despliegue de Plane en un VPS: gestión de proyectos self-hosted, SSL y copias de seguridad

TL;DR

Plane es una plataforma self-hosted para gestionar proyectos, tareas, sprints, notas y equipos. A continuación, desplegaremos Plane en Docker sobre Ubuntu 24.04 LTS, lo protegeremos detrás de Caddy con un certificado SSL automático, configuraremos la seguridad básica del servidor y realizaremos copias de seguridad de la base de datos, la configuración y los archivos de usuario.

  • Usaremos un VPS con al menos 4 vCPU, 8 GB de RAM y SSD de 80 GB o más.
  • Instalaremos Docker Engine y Docker Compose Plugin desde el repositorio oficial.
  • Desplegaremos Plane mediante el instalador self-hosted oficial y fijaremos la versión de lanzamiento.
  • Abriremos únicamente SSH, HTTP y HTTPS, y restringiremos el acceso administrativo a claves SSH.
  • Configuraremos Caddy para obtener y renovar automáticamente el certificado TLS.
  • Crearemos un backup diario de PostgreSQL, la configuración y los datos del almacenamiento de objetos.

1. TL;DR

Esta guía cubre el ciclo completo de despliegue de Plane: desde un VPS limpio y la protección básica de Ubuntu hasta la publicación de la aplicación mediante HTTPS y la restauración desde una copia de seguridad. Los comandos están diseñados para Ubuntu Server 24.04 LTS y Docker Engine 28.x o una versión estable más reciente disponible en el repositorio oficial de Docker en el momento de la instalación.

  • Plane funciona como un conjunto de contenedores: interfaz web, API, tareas en segundo plano, PostgreSQL, Redis y almacenamiento de archivos.
  • El nombre de dominio debe apuntar a la dirección IPv4 pública del servidor antes de iniciar Caddy.
  • Los secretos se almacenan en el archivo .env con permisos de acceso restringidos.
  • Un backup debe incluir no solo PostgreSQL, sino también los archivos cargados, la configuración y las claves.

2. Contenido

El artículo está estructurado como un escenario práctico para el propietario de un VPS nuevo. Primero se definen los recursos y requisitos; después se preparará el servidor, se instalará Plane, se publicará mediante HTTPS y se configurarán copias de seguridad periódicas.

  1. Preparar un registro DNS para el dominio.
  2. Crear un administrador independiente y deshabilitar el inicio de sesión con contraseña.
  3. Instalar Docker y las utilidades del sistema.
  4. Desplegar Plane desde la fuente oficial.
  5. Configurar el reverse proxy externo y TLS.
  6. Comprobar la aplicación y automatizar el backup.

3. Qué configuramos y por qué

Схема: 3. Что мы настраиваем и зачем
Esquema: 3. Qué configuramos y por qué

Qué es Plane

Plane es un sistema de gestión de desarrollo y proyectos de código abierto. Proporciona espacios de trabajo, proyectos, tareas, estados, ciclos, módulos, vistas, comentarios, páginas y funciones básicas de colaboración en equipo. La interfaz es adecuada tanto para un pequeño equipo de producto como para varios proyectos independientes de una misma organización.

La variante self-hosted se ejecuta en la infraestructura del propietario. Los datos se almacenan en su servidor y se accede a la aplicación a través de su propio dominio. Esto resulta especialmente conveniente cuando es necesario controlar la ubicación de los datos, las reglas de red, los períodos de retención o la integración con servicios internos.

Qué funcionará después de la configuración

Después de seguir las instrucciones, el usuario podrá abrir, por ejemplo, https://plane.example.com, crear un espacio de trabajo e invitar a miembros del equipo. Plane se ejecutará en contenedores Docker y Caddy recibirá el tráfico HTTPS externo y lo enviará a la aplicación mediante la interfaz local.

En el servidor también estarán disponibles:

  • PostgreSQL para los datos persistentes de la aplicación;
  • Redis para colas y almacenamiento en caché;
  • almacenamiento de objetos para archivos adjuntos y archivos de usuario;
  • Docker volumes para los datos que deben persistir tras la recreación de los contenedores;
  • registros de contenedores y registros del sistema para diagnóstico.

Cloud-managed o self-hosted

Criterio Versión en la nube Self-hosted en VPS
Instalación Prácticamente no se requiere Es necesario mantener el servidor y los contenedores
Control de datos Depende del proveedor Los datos están bajo el control del propietario
Coste Normalmente depende del número de usuarios El gasto principal es el servidor, los discos y el backup
Actualizaciones Se realizan automáticamente o por el proveedor Se deben planificar de forma independiente
Integración Limitada por las capacidades del servicio Se pueden configurar VPN, SSO, SMTP y webhook internos

La opción cloud-managed es más razonable si no hay tiempo para operar un servidor Linux. Se elige Plane self-hosted en un VPS cuando son importantes el control, una infraestructura predecible, la independencia de los límites de SaaS o el alojamiento junto a otros servicios internos.

Requisitos previos

Se necesita un dominio o subdominio, por ejemplo, plane.example.com. Cree un registro DNS de tipo A que apunte a la IPv4 del servidor. Si se utiliza IPv6, agregue un registro AAAA solo después de verificar que el servidor y el firewall gestionan IPv6 correctamente.

# Comprobamos que DNS ya apunta a la dirección correcta
dig +short plane.example.com A

Antes de obtener el certificado, el dominio debe estar accesible desde Internet a través de los puertos 80 y 443. Si hay un firewall externo delante del servidor, sus reglas también deben permitir estos puertos.

4. Qué configuración de VPS se necesita para esta tarea

Схема: 4. Какой VPS-конфиг нужен под эту задачу
Esquema: 4. Qué configuración de VPS se necesita para esta tarea

Plane no es una página HTML estática. Una instalación self-hosted ejecuta varios contenedores, una base de datos, una cola de tareas y almacenamiento de archivos. Por ello, no es correcto guiarse únicamente por el tamaño de la interfaz web: son importantes al mismo tiempo la RAM, un disco rápido y margen de CPU para operaciones en segundo plano.

Requisitos mínimos

Recurso Mínimo para un equipo de prueba Punto de partida práctico para producción
CPU 2 vCPU 4 vCPU
RAM 4 GB 8 GB
Disco 40 GB SSD 80–160 GB NVMe SSD
Red 100 Mbit/s 1 Gbit/s o superior
Dirección IPv4 pública IPv4 pública y, si es necesario, IPv6
SO Ubuntu 24.04 LTS Ubuntu 24.04 LTS

Una configuración con 4 vCPU y 8 GB de RAM es adecuada para un equipo pequeño, varios proyectos y un número moderado de archivos adjuntos. El disco debe elegirse teniendo en cuenta el crecimiento de los archivos: la base de datos de Plane normalmente ocupa menos espacio que las imágenes, documentos y otros adjuntos.

Como una de las opciones, puede contratar un VPS con 4 vCPU, 8 GB de RAM, un disco NVMe de 80 GB o más y una IPv4 pública. Se pueden obtener parámetros similares con cualquier proveedor que ofrezca acceso root completo, virtualización con recursos garantizados y la posibilidad de realizar backups externos.

Cuándo se necesita un dedicated

Un servidor dedicated se justifica no por el simple hecho de ejecutar Plane, sino por la carga y los requisitos de aislamiento. Es necesario si Plane, GitLab, CI runners, monitorización, bases de datos y otros servicios que consumen muchos recursos funcionan simultáneamente en un mismo servidor, o si se requiere rendimiento garantizado sin competencia por CPU y disco.

Para un equipo de varias decenas de personas, un dedicated normalmente no es obligatorio. Primero conviene medir el consumo de CPU, RAM, I/O y el tamaño de los datos. Migrar a un servidor dedicado tiene sentido cuando aparece una carga constante, uso frecuente de swap o la necesidad de alojar varios sistemas de producción.

Elección de ubicación

La ubicación influye en la latencia de acceso, los requisitos legales y el coste de transferencia de datos. Para un equipo ubicado en una misma región, elija un centro de datos con el menor RTT posible hacia los usuarios. Para un equipo internacional, una ruta estable y la disponibilidad de IPv4 son más importantes que una diferencia de unos pocos milisegundos.

Es recomendable almacenar las copias de seguridad en otra zona geográfica o al menos en otro servidor físico. Un backup en el mismo VPS no protege contra la eliminación del disco, el bloqueo de la cuenta, un fallo de hardware o un error del administrador.

5. Preparación del servidor

Conexión y creación del administrador

Se supone que el proveedor ha proporcionado un servidor Ubuntu 24.04 LTS limpio y acceso inicial mediante SSH. Sustituya la dirección IP y el nombre de usuario creados durante la instalación.

# Nos conectamos al servidor mediante SSH
ssh root@SERVER_IP

Crearemos el usuario deploy, lo añadiremos al grupo sudo e instalaremos la clave pública. Ejecute el bloque como root.

# Creamos un administrador independiente
adduser deploy

# Permitimos al usuario ejecutar comandos administrativos
usermod -aG sudo deploy

# Creamos un directorio para las claves SSH
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh

# Abrimos el editor para añadir la clave pública
nano /home/deploy/.ssh/authorized_keys

# Corregimos el propietario y los permisos del archivo de claves
chown deploy:deploy /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys

Pegue en authorized_keys el contenido del archivo id_ed25519.pub de su equipo. Después, compruebe el nuevo inicio de sesión en una terminal independiente sin cerrar la sesión actual de root.

# Comprobamos el inicio de sesión con el nuevo usuario
ssh deploy@SERVER_IP

# Comprobamos los permisos sudo
sudo -v

Actualización del sistema y utilidades básicas

# Actualizamos los índices de paquetes e instalamos correcciones
sudo apt update && sudo apt full-upgrade -y

# Instalamos herramientas de administración y diagnóstico
sudo apt install -y ca-certificates curl gnupg lsb-release \
  unzip jq git vim nano htop tree dnsutils \
  ufw fail2ban unattended-upgrades

Reinicie el servidor si se actualizó el kernel o las bibliotecas del sistema.

# Comprobamos si es necesario reiniciar
if [ -f /var/run/reboot-required ]; then sudo reboot; fi

Claves SSH y desactivación de contraseñas

Después de comprobar el inicio de sesión mediante clave, desactive la autenticación por contraseña. Antes de modificar la configuración, guarde una copia del archivo y compruebe la sintaxis de SSH.

# Guardamos una copia de seguridad de la configuración SSH
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

# Abrimos la configuración SSH
sudo nano /etc/ssh/sshd_config

Asegúrese de que estén presentes los siguientes parámetros:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
# Comprobamos la configuración antes de reiniciar
sudo sshd -t

# Aplicamos la configuración SSH
sudo systemctl restart ssh

Firewall

Abriremos SSH, HTTP y HTTPS. Si SSH funciona en un puerto no estándar, sustituya 22/tcp por el valor correspondiente. Primero permita SSH y solo después active UFW.

# Permitimos el acceso administrativo y el tráfico web
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Activamos el firewall con una política de denegación de conexiones entrantes
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw --force enable

# Comprobamos las reglas activas
sudo ufw status verbose

Fail2ban

Fail2ban bloquea temporalmente las direcciones desde las que se realizan intentos fallidos repetidos de inicio de sesión en SSH. No sustituye las claves ni el firewall, pero reduce el ruido de los escáneres automáticos.

# Creamos una configuración local de jail para SSH
sudo tee /etc/fail2ban/jail.d/sshd.local > /dev/null <<'EOF'
[sshd]
enabled = true
backend = systemd
port = 22
maxretry = 5
findtime = 10m
bantime = 1h
EOF

# Reiniciamos fail2ban y activamos el inicio automático
sudo systemctl enable --now fail2ban

# Comprobamos el estado de la protección SSH
sudo fail2ban-client status sshd

6. Instalación del software — paso a paso

Схема: 6. Установка ПО — пошагово
Esquema: 6. Instalación del software — paso a paso

Instalación de Docker Engine

En Ubuntu es mejor utilizar el repositorio apt oficial de Docker, en lugar de los paquetes antiguos del repositorio estándar. En 2026, utilice la rama estable actual de Docker Engine 28.x o una más reciente, si ya se ha publicado para Ubuntu 24.04.

# Eliminamos paquetes no oficiales conflictivos
sudo apt remove -y docker.io docker-doc docker-compose podman-docker containerd runc || true

# Creamos el directorio para las claves apt
sudo install -m 0755 -d /etc/apt/keyrings

# Descargamos la clave oficial de Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Hacemos que la clave sea accesible para apt
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Añadimos el repositorio Docker para la versión actual de Ubuntu
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Actualizamos la lista de paquetes e instalamos Docker Compose Plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Permitimos ejecutar Docker sin sudo al usuario deploy
sudo usermod -aG docker "$USER"

# Comprobamos las versiones
docker --version
docker compose version

Después de añadir el usuario al grupo Docker, debe abrir una nueva sesión SSH. El grupo docker proporciona de facto permisos de root, así que añada a él solo administradores de confianza.

# Comprobamos el funcionamiento de Docker tras iniciar sesión de nuevo
docker run --rm hello-world

Descarga del instalador oficial de Plane

La distribución self-hosted de Plane es publicada por el proyecto Plane en GitHub. El instalador crea el directorio de despliegue, los archivos Docker Compose y una plantilla de variables de entorno. Antes de aplicarlo en producción, compruebe las release notes y la compatibilidad de la versión elegida.

# Vamos al directorio principal del administrador
cd ~

# Creamos un directorio independiente para Plane
mkdir -p ~/plane
cd ~/plane

# Descargamos el instalador oficial desde el repositorio de Plane
curl -fsSL -o setup.sh \
  https://raw.githubusercontent.com/makeplane/plane/master/deploy/selfhost/install.sh

# Hacemos ejecutable el instalador
chmod 700 setup.sh

# Revisamos el instalador antes de ejecutarlo
less setup.sh

La revisión del script antes de ejecutarlo es obligatoria: muestra qué imágenes, directorios y comandos se utilizarán. Si el proyecto ha publicado un instalador para una versión concreta, es preferible obtenerlo desde la página de la versión en lugar de utilizar la rama flotante master.

# Ejecutamos la instalación interactiva de Plane
./setup.sh install

Según la versión del instalador, el comando puede llamarse ./setup.sh install, ./setup.sh start o tener un menú de acciones. Utilice el nombre que muestre el propio script al ejecutarlo con el parámetro de ayuda.

# Mostramos las acciones disponibles de la versión concreta del instalador
./setup.sh --help

Obtención de la configuración inicial

Normalmente, en el directorio de Plane aparecen el archivo .env y uno o varios archivos Compose. Si el instalador creó solo una plantilla, cópiela al archivo de trabajo y complete los valores.

# Mostramos el contenido del directorio de despliegue
find ~/plane -maxdepth 2 -type f -printf '%p\n' | sort

# Si hay una plantilla, creamos un archivo de entorno de trabajo
[ -f ~/plane/.env.example ] && cp ~/plane/.env.example ~/plane/.env

# Restringimos el acceso a los secretos
chmod 600 ~/plane/.env

Inicio de los contenedores

Antes del primer inicio, estudie la configuración final. Esto permite ver los nombres de los servicios, los puertos expuestos y los volumes que deben incluirse en la copia de seguridad.

# Vamos al directorio de Plane
cd ~/plane

# Comprobamos y desplegamos la configuración Compose final
docker compose config > /tmp/plane-compose-resolved.yml

# Iniciamos los servicios en segundo plano
docker compose up -d

El primer inicio puede tardar varios minutos: Docker descarga las imágenes, crea la red y los volumes, y la aplicación ejecuta las migraciones de la base de datos.

# Comprobamos la lista de contenedores y su estado
docker compose ps

# Vemos las últimas 200 líneas del registro general
docker compose logs --tail=200

# Seguimos el registro de un servicio específico si es necesario
docker compose logs -f --tail=100 web

El nombre del servicio web depende de la versión del archivo Compose. Si no existe dicho servicio, ejecute primero docker compose config --services y seleccione el nombre real del servicio frontend o proxy.

7. Configuración

Diagrama: 7. Configuración
Diagrama: 7. Configuración

Variables de entorno

Todos los secretos de Plane deben estar en .env o en un gestor de secretos protegido. No inserte contraseñas de PostgreSQL, claves JWT ni contraseñas SMTP directamente en el archivo Compose, el repositorio Git o un script de shell accesible para todos los usuarios.

Los nombres de las variables dependen de la versión específica de Plane. No elimine los valores obligatorios del archivo creado por el installer. Complete el dominio y genere secretos aleatorios donde lo prevea la plantilla.

# Generamos valores criptográficamente aleatorios para nuestros propios secretos
openssl rand -hex 32
openssl rand -base64 48

# Abrimos el archivo de variables de entorno
nano ~/plane/.env

# Verificamos los permisos del archivo de secretos
stat -c '%A %U:%G %n' ~/plane/.env

La configuración debe definir correctamente la URL pública de la aplicación, el dominio, los parámetros de PostgreSQL, Redis, SMTP y el almacenamiento de objetos. La URL pública debe usar la dirección HTTPS definitiva, por ejemplo:

WEB_URL=https://plane.example.com
CORS_ALLOWED_ORIGINS=https://plane.example.com

Los nombres exactos de las variables deben verificarse con la documentación y la plantilla de la versión elegida. No añada mecánicamente variables inexistentes: la aplicación podría ignorarlas y seguir funcionando con valores predeterminados inseguros.

Publicación mediante Caddy

Caddy obtiene automáticamente un certificado de Let’s Encrypt y lo renueva. Primero puede ejecutar Plane en un puerto local no accesible desde Internet y exponer al exterior únicamente Caddy.

Compruebe qué servicio de Plane publica el puerto HTTP:

# Mostramos los servicios y los puertos publicados
cd ~/plane
docker compose config --services
docker compose ps

Si el archivo Compose publica el puerto 80 del contenedor en el puerto 80 del host, cambie la vinculación a una dirección local y un puerto libre, por ejemplo 127.0.0.1:8080:80. Hágalo en un archivo override compatible o mediante el método previsto por el installer. El ejemplo override siguiente solo se aplica si el servicio realmente se llama proxy y escucha el puerto 80 internamente.

services:
  proxy:
    ports:
      - "127.0.0.1:8080:80"

Si el servicio interno tiene otro nombre, sustituya proxy. No exponga directamente al exterior PostgreSQL, Redis, MinIO ni puertos administrativos.

# Aplicamos el cambio y recreamos los contenedores
cd ~/plane
docker compose up -d

# Verificamos la respuesta HTTP local
curl -I http://127.0.0.1:8080

Instalemos Caddy desde el repositorio oficial.

# Añadimos la clave oficial del repositorio de Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

# Añadimos el repositorio de Caddy
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list

# Instalamos Caddy 2.x
sudo apt update
sudo apt install -y caddy

Cree la configuración /etc/caddy/Caddyfile. Sustituya el dominio por el suyo.

plane.example.com {
    reverse_proxy 127.0.0.1:8080

    encode zstd gzip

    header {
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy strict-origin-when-cross-origin
    }

    log {
        output file /var/log/caddy/plane-access.log
        format json
    }
}
# Verificamos la sintaxis de Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile

# Habilitamos el inicio de Caddy junto con el sistema
sudo systemctl enable --now caddy

# Reiniciamos después de cambiar la configuración
sudo systemctl reload caddy

# Verificamos el estado del proxy inverso
sudo systemctl status caddy --no-pager

Si DNS ya está configurado, Caddy enviará automáticamente una solicitud para obtener el certificado. La comprobación debe realizarse desde un equipo externo o mediante el dominio público:

# Verificamos HTTPS y las cabeceras de respuesta
curl -I https://plane.example.com

# Verificamos el certificado TLS
curl -vI https://plane.example.com 2>&1 | grep -E 'SSL connection|subject:|issuer:'

Comprobación del estado de Plane

Abra el dominio en el navegador, cree la cuenta inicial y compruebe la creación de proyectos, tareas y adjuntos. A continuación, revise los contenedores y los últimos errores.

# Verificamos que los contenedores no estén en estado restarting
cd ~/plane
docker compose ps

# Buscamos errores en los registros desde el último inicio
docker compose logs --since=10m 2>&1 | grep -iE 'error|fatal|panic' || true

# Verificamos el espacio en disco
df -h
docker system df

El comando ping solo comprueba la conectividad ICMP y no confirma el funcionamiento de HTTPS. Para la aplicación es más útil utilizar curl, comprobar el estado de los contenedores y revisar los registros.

8. Copias de seguridad y mantenimiento

Qué se debe guardar

La copia de seguridad mínima de Plane consta de cuatro partes: un volcado de PostgreSQL, archivos de usuario, el archivo .env y la configuración de Compose. Si se utiliza MinIO u otro almacenamiento compatible con S3, su bucket con adjuntos no puede sustituirse únicamente por un volcado de la base de datos.

  • PostgreSQL: espacios de trabajo, proyectos, tareas, usuarios y configuraciones.
  • Object storage: imágenes, documentos, avatares y adjuntos.
  • Configuración: .env, archivos Compose y Caddyfile.
  • Claves: solo si son necesarias para el descifrado o el acceso a la copia de seguridad.

No confíe en un snapshot de VPS como única copia de seguridad. Un snapshot es útil para una reversión rápida, pero si hay un problema con la propia cuenta o el almacenamiento, puede volverse inaccesible junto con el servidor.

Instalación de restic

Restic cifra la copia de seguridad antes de enviarla al almacenamiento externo. El ejemplo utiliza un bucket compatible con S3. Cree el bucket con antelación, un usuario independiente con permisos mínimos y guarde la contraseña del repositorio fuera del servidor o en un almacenamiento de secretos protegido.

# Instalamos restic desde el repositorio de Ubuntu
sudo apt update
sudo apt install -y restic

# Creamos directorios para volcados temporales y scripts
sudo install -d -m 700 /var/backups/plane
sudo install -d -m 700 /usr/local/sbin

Script de copia de seguridad

Los nombres de los servicios de base de datos y almacenamiento de objetos deben determinarse mediante la salida de docker compose config --services. En el ejemplo, la base se llama plane-db. Si en su versión tiene otro nombre, cambie la variable DB_SERVICE.

sudo nano /usr/local/sbin/plane-backup.sh
#!/usr/bin/env bash
set -Eeuo pipefail

PLANE_DIR="/home/deploy/plane"
BACKUP_DIR="/var/backups/plane"
STAMP="$(date -u +%Y-%m-%dT%H-%M-%SZ)"
DB_SERVICE="plane-db"

# Es mejor cargar estas variables desde un archivo independiente accesible solo por root
source /root/.config/plane-backup/restic.env

mkdir -p "$BACKUP_DIR/$STAMP"

# Creamos un volcado lógico de PostgreSQL dentro del contenedor
cd "$PLANE_DIR"
docker compose exec -T "$DB_SERVICE" \
  pg_dumpall -U postgres | gzip -9 > "$BACKUP_DIR/$STAMP/postgres.sql.gz"

# Guardamos la configuración y los archivos Compose
tar --exclude='.log' -czf "$BACKUP_DIR/$STAMP/config.tar.gz" \
  -C "$PLANE_DIR" .env docker-compose.yml docker-compose.yaml 2>/dev/null || true

# Guardamos Caddyfile
tar -czf "$BACKUP_DIR/$STAMP/caddy.tar.gz" \
  -C /etc caddy/Caddyfile

# Enviamos los datos cifrados al almacenamiento S3 externo
restic backup "$BACKUP_DIR/$STAMP" \
  --tag plane \
  --host "$(hostname -f)"

# Eliminamos archivos temporales locales con más de dos días
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +2 -exec rm -rf {} +

# Eliminamos copias de seguridad remotas antiguas según la política de retención
restic forget --tag plane --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Cree el archivo con los parámetros de restic. Solo root debe tener acceso a él.

# Creamos el directorio para los parámetros de copia de seguridad
sudo install -d -m 700 /root/.config/plane-backup

# Creamos el archivo con la URL de S3 y la contraseña de restic
sudo nano /root/.config/plane-backup/restic.env

# Ejemplo de contenido; sustituya los valores por los suyos
RESTIC_REPOSITORY="s3:https://s3.example.net/plane-backups"
RESTIC_PASSWORD="GENERATE_AND_STORE_A_LONG_RANDOM_PASSWORD"
AWS_ACCESS_KEY_ID="BACKUP_ACCESS_KEY"
AWS_SECRET_ACCESS_KEY="BACKUP_SECRET_KEY"

# Restringimos los permisos
sudo chmod 600 /root/.config/plane-backup/restic.env

# Hacemos ejecutable el script
sudo chmod 700 /usr/local/sbin/plane-backup.sh

# Inicializamos el repositorio una vez
sudo bash -c 'source /root/.config/plane-backup/restic.env && restic init'

Pruebe la copia de seguridad manualmente antes de añadir cron.

# Ejecutamos una copia de seguridad completa y verificamos el código de retorno
sudo /usr/local/sbin/plane-backup.sh

# Mostramos la lista de snapshots de restic
sudo bash -c 'source /root/.config/plane-backup/restic.env && restic snapshots --tag plane'

Planificador cron

Para una instalación pequeña basta con una ejecución diaria por la noche. Si se crean muchos datos durante la jornada laboral, reduzca el intervalo y habilite por separado la copia de seguridad de la base de datos. Lo más importante es comprobar periódicamente la restauración, no solo la existencia de archivos.

# Abrimos el crontab de root
sudo crontab -e
# Todos los días a las 02:30 según la hora del servidor
30 2    /usr/local/sbin/plane-backup.sh >> /var/log/plane-backup.log 2>&1

Comprobación de la restauración

No pruebe la restauración sobre la única base de datos de producción. Cree un servidor temporal o un proyecto Compose independiente, restaure el último snapshot, importe PostgreSQL y compruebe el inicio de sesión, los proyectos y los adjuntos.

# Verificamos la integridad de los datos de restic
sudo bash -c 'source /root/.config/plane-backup/restic.env && restic check'

# Mostramos el contenido del último snapshot
sudo bash -c 'source /root/.config/plane-backup/restic.env && restic ls latest --tag plane'

Actualizaciones de Plane

Antes de actualizar, lea las release notes de la versión específica y haga una copia de seguridad. Para un equipo pequeño es más seguro usar una ventana de mantenimiento: detener las escrituras, actualizar las imágenes, esperar las migraciones y comprobar los escenarios principales.

# Creamos una copia de seguridad antes de la actualización
sudo /usr/local/sbin/plane-backup.sh

# Descargamos nuevas imágenes y recreamos los contenedores
cd /home/deploy/plane
docker compose pull
docker compose up -d

# Verificamos las migraciones y el estado de los servicios
docker compose ps
docker compose logs --tail=200

No use sin pensar docker system prune -a: el comando puede eliminar imágenes necesarias para una reversión rápida. No elimine volumes hasta que se confirme la existencia de una copia de seguridad externa y se conozca el proceso de restauración.

9. Solución de problemas y preguntas frecuentes

¿Por qué el dominio se abre con un error 502 Bad Gateway?

Primero compruebe que Caddy esté funcionando: systemctl status caddy. Luego asegúrese de que Plane realmente escuche en el puerto local con el comando curl -I http://127.0.0.1:8080. Si no hay respuesta, ejecute docker compose ps y docker compose logs --tail=200 en el directorio de Plane. Una causa frecuente es un nombre de servicio incorrecto en el archivo override, un contenedor detenido o que Caddy apunte a un puerto que no está publicado en el host.

El certificado de Caddy no se emite. ¿Qué comprobar?

Compruebe el registro A con el comando dig +short plane.example.com y compare la dirección con la IP pública del servidor. Los puertos 80 y 443 deben estar permitidos en UFW, el firewall externo y el security group del proveedor. Si el proxy CDN está activado, asegúrese de que no bloquee la comprobación HTTP o utilice temporalmente una configuración DNS sin proxy. La causa detallada se encuentra en journalctl -u caddy -e.

Los contenedores de Plane se reinician constantemente. ¿Qué hacer?

Revise el estado y el código de salida: docker compose ps, luego los registros del contenedor específico: docker compose logs --tail=300 SERVICE. Compruebe la RAM y el disco disponibles mediante free -h y df -h. Las causas típicas son falta de memoria, secretos incorrectos, una base de datos inaccesible, variables obligatorias sin completar o un volume dañado. No elimine volumes antes de analizar los registros y comprobar el backup.

¿Qué configuración de VPS es mínimamente adecuada?

Para una instalación de prueba, puede empezar con 2 vCPU, 4 GB de RAM y 40 GB de SSD, pero esto deja poco margen para Docker, la base de datos y las actualizaciones. Una opción mínima práctica para producción es 4 vCPU, 8 GB de RAM y SSD desde 80 GB. Si se prevén archivos adjuntos grandes, el tamaño del disco debe elegirse según la previsión de crecimiento de datos y los backup deben almacenarse por separado. Para un funcionamiento estable, un SSD rápido y recursos garantizados son más importantes que una gran cantidad de núcleos virtuales.

¿Qué elegir: VPS o dedicado para esta tarea?

Para una instancia de Plane y un equipo pequeño, normalmente basta con un VPS. Un servidor dedicado es necesario cuando el servidor también atiende tareas de CI pesadas, GitLab, bases de datos, monitorización y otras aplicaciones, o cuando se requiere aislamiento total de recursos. Es mejor tomar la decisión según las mediciones: si la RAM se agota constantemente, aparece swap y el disco tiene una alta carga de I/O, primero puede ampliar el VPS y luego considerar un dedicado.

¿Se puede no usar Caddy y dejar el reverse proxy integrado?

Es posible, si el deployment oficial de Plane ya proporciona un contenedor proxy compatible y una configuración TLS correcta. Caddy externo es conveniente porque separa la gestión del certificado de la aplicación y permite no publicar servicios internos. No se puede ocupar simultáneamente el puerto 80 con varios proxy: elija una única capa de entrada externa. En cualquier caso, solo deben estar abiertos los puertos 80 y 443, y las bases de datos y colas deben permanecer en la red interna de Docker.

¿Dónde ver la causa de un error de inicio de sesión o de una tarea que no funciona?

Compruebe los registros del frontend, la API y los workers en segundo plano, no solo los de Caddy. La lista de nombres exactos de servicios se muestra con el comando docker compose config --services. Luego use docker compose logs --since=15m SERVICE. Si el error está relacionado con correos, compruebe las variables SMTP y la disponibilidad del servidor de envío. Si los archivos adjuntos no se cargan, revise la configuración del almacenamiento de objetos y el espacio libre disponible.

¿Qué hacer si se agota el disco?

Primero identifique el origen: df -h, du -xhd1 /var/lib/docker y docker system df. Compruebe el tamaño de uploads, PostgreSQL y los registros. Los backup locales antiguos solo pueden eliminarse después de confirmar que la copia externa está disponible. Los registros de Docker deben limitarse mediante parámetros compatibles de Compose o la configuración del daemon. No elimine volumes ni use aggressive prune sin comprender qué datos se almacenan en cada volumen.

¿Cómo actualizar Plane de forma segura sin tiempo de inactividad?

Una actualización completamente ininterrumpida depende de la versión específica de Plane y del esquema de la base de datos, por lo que para un único VPS es mejor utilizar una breve ventana de mantenimiento. Realice un backup externo, revise las release notes, descargue las nuevas imágenes, aplique docker compose up -d y compruebe las migraciones. Para minimizar el tiempo de inactividad, puede descargar las imágenes con antelación mediante docker compose pull. Antes de actualizar, guarde el tag actual de las imágenes y los archivos Compose para poder revertir.

10. Conclusiones y próximos pasos

Diagrama: 10. Conclusiones y próximos pasos
Diagrama: 10. Conclusiones y próximos pasos

Como resultado, Plane funciona en su propio VPS en Docker, está disponible mediante HTTPS a través de Caddy y el servidor está protegido con claves SSH, UFW y fail2ban. La configuración, PostgreSQL y los archivos de usuario están incluidos en un backup externo cifrado con comprobación regular de restauración.

A continuación, es útil activar la monitorización de CPU, RAM, disco y la vigencia del backup, y luego trasladar el almacenamiento de objetos a un servicio independiente compatible con S3 a medida que crezcan los archivos adjuntos. Cuando el equipo crezca, puede añadir SMTP, SSO, un servidor de base de datos independiente o escalar el VPS tras analizar el consumo real de recursos.

¿Te fue útil esta guía?

Tus comentarios nos ayudan a mejorar nuestras guías.

Compartir esta publicación:

Envía esta guía a alguien a quien pueda resultarle útil.

Telegram VKVK WhatsApp Facebook LinkedIn XX

despliegue de Plane en VPS: gestión de proyectos autoalojada, SSL y copias de seguridad
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.