SurrealDB es una base de datos multimodelo: guarda documentos JSON, permite relaciones de grafo entre registros y se consulta con SurrealQL, un lenguaje parecido a SQL. Un único binario expone una API HTTP y WebSocket en el puerto 8000. En este tutorial instalarás SurrealDB en Ubuntu 24.04, lo ejecutarás como servicio systemd con almacenamiento persistente en RocksDB, crearás un usuario para tu aplicación y probarás consultas de documentos y de grafo.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS (x86_64 o arm64), por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Al menos 1 GB de RAM y unos GB libres en disco para los datos.
  • curl instalado (sudo apt install curl).

Paso 1: Instalar el binario de SurrealDB

SurrealDB publica un script de instalación que detecta la arquitectura, descarga la última versión estable desde download.surrealdb.com y copia el binario surreal en /usr/local/bin. Descárgalo primero para poder revisarlo antes de ejecutarlo:

curl -sSf https://install.surrealdb.com -o install-surreal.sh
less install-surreal.sh

Ejecútalo con sudo para que pueda escribir en /usr/local/bin:

sudo sh install-surreal.sh

Comprueba la versión instalada:

surreal version
3.3.0 for linux on x86_64

Tu número de versión puede ser más reciente. Si en lugar de /usr/local/bin el script instaló el binario en ~/.surrealdb/surreal, muévelo con sudo mv ~/.surrealdb/surreal /usr/local/bin/.

Paso 2: Crear el usuario del sistema y el directorio de datos

El servicio no debe ejecutarse como root. Crea un usuario de sistema sin shell y el directorio donde RocksDB guardará los datos:

sudo useradd --system --home-dir /var/lib/surrealdb --shell /usr/sbin/nologin surrealdb
sudo install -d -o surrealdb -g surrealdb -m 0750 /var/lib/surrealdb

Paso 3: Guardar las credenciales del usuario root

Al arrancar por primera vez, surreal start crea el usuario root de la base de datos a partir de las variables SURREAL_USER y SURREAL_PASS. Guárdalas en un archivo que solo pueda leer root, en lugar de ponerlas en la línea de comandos donde las vería cualquiera con ps:

sudo nano /etc/surrealdb.env
SURREAL_USER=root
SURREAL_PASS=your_strong_password
SURREAL_BIND=127.0.0.1:8000
SURREAL_PATH=rocksdb:/var/lib/surrealdb/data
SURREAL_LOG=info

Sustituye your_strong_password por una contraseña larga y aleatoria. SURREAL_BIND hace que el servidor escuche solo en localhost; lo abrirás al exterior más adelante si lo necesitas. Protege el archivo:

sudo chmod 600 /etc/surrealdb.env

Paso 4: Crear el servicio systemd

Crea la unidad del servicio:

sudo nano /etc/systemd/system/surrealdb.service
[Unit]
Description=SurrealDB
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=surrealdb
Group=surrealdb
EnvironmentFile=/etc/surrealdb.env
ExecStart=/usr/local/bin/surreal start
Restart=on-failure
LimitNOFILE=65536
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/surrealdb

[Install]
WantedBy=multi-user.target

surreal start lee la ruta de datos, la dirección de escucha y las credenciales de las variables de entorno del archivo anterior. Recarga systemd y arranca el servicio:

sudo systemctl daemon-reload
sudo systemctl enable --now surrealdb
sudo systemctl status surrealdb --no-pager
● surrealdb.service - SurrealDB
     Loaded: loaded (/etc/systemd/system/surrealdb.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-09-25 10:12:03 UTC; 4s ago

Comprueba que responde al endpoint de salud:

curl -i http://127.0.0.1:8000/health
HTTP/1.1 200 OK
surreal-version: surrealdb/3.3.0
server: SurrealDB

Si el servicio no arranca, revisa el registro con sudo journalctl -u surrealdb -n 50.

Paso 5: Conectarte con el cliente SQL

SurrealDB organiza los datos en espacios de nombres (namespaces) que contienen bases de datos, y estas contienen tablas. El cliente surreal sql abre una consola interactiva; indica el namespace y la base de datos con --ns y --db (se crean automáticamente al usarlos):

surreal sql -e http://127.0.0.1:8000 -u root -p your_strong_password --ns tienda --db principal --pretty

Verás un prompt como tienda/principal>. Todas las consultas de los pasos siguientes se escriben aquí. Para salir, pulsa Ctrl+C.

Paso 6: Definir tablas y crear documentos

Por defecto las tablas son sin esquema (schemaless) y aceptan cualquier campo. Para datos de aplicación suele convenir un esquema estricto con tipos y validaciones. Define una tabla producto con tres campos y un índice único:

DEFINE TABLE producto SCHEMAFULL;
DEFINE FIELD nombre ON producto TYPE string;
DEFINE FIELD precio ON producto TYPE number ASSERT $value >= 0;
DEFINE FIELD etiquetas ON producto TYPE array<string> DEFAULT [];
DEFINE INDEX producto_nombre ON producto FIELDS nombre UNIQUE;

Crea algunos registros. En SurrealDB cada registro tiene un identificador tabla:id, que puedes elegir tú:

CREATE producto:teclado SET nombre = 'Teclado mecánico', precio = 89.9, etiquetas = ['periféricos'];
CREATE producto:raton SET nombre = 'Ratón inalámbrico', precio = 29.5;
CREATE cliente:ana CONTENT { nombre: 'Ana', ciudad: 'Barcelona' };

La tabla cliente no tiene esquema definido, así que acepta el documento tal cual. Consulta los productos baratos:

SELECT nombre, precio FROM producto WHERE precio < 50;
[
	{
		nombre: 'Ratón inalámbrico',
		precio: 29.5f
	}
]

Comprueba que la validación funciona intentando guardar un precio negativo:

CREATE producto:erroneo SET nombre = 'Prueba', precio = -1;
'Found -1 for field `precio`, with record `producto:erroneo`, but field must conform to: $value >= 0'

Paso 7: Relacionar registros como un grafo

RELATE crea una arista entre dos registros y la guarda en su propia tabla (aquí compro), que puede tener campos como cualquier otro registro:

RELATE cliente:ana->compro->producto:teclado SET fecha = time::now(), cantidad = 1;
RELATE cliente:ana->compro->producto:raton SET fecha = time::now(), cantidad = 2;

Recorre el grafo con flechas: ->compro->producto sigue las aristas salientes y <-compro<-cliente las entrantes.

SELECT nombre, ->compro->producto.nombre AS compras FROM cliente:ana;
[
	{
		compras: [
			'Ratón inalámbrico',
			'Teclado mecánico'
		],
		nombre: 'Ana'
	}
]
SELECT nombre, <-compro<-cliente.nombre AS compradores FROM producto:teclado;
[
	{
		compradores: [
			'Ana'
		],
		nombre: 'Teclado mecánico'
	}
]

Esta es la principal diferencia frente a una base de datos relacional: no necesitas tablas intermedias ni JOIN para seguir relaciones de varios saltos.

Paso 8: Crear un usuario para la aplicación

Tu aplicación no debe usar el root. Crea un usuario limitado a la base de datos principal con el rol EDITOR, que puede leer y escribir datos pero no gestionar usuarios:

DEFINE USER app ON DATABASE PASSWORD 'app_strong_password' ROLES EDITOR;

Los roles disponibles son OWNER, EDITOR y VIEWER. Sal de la consola y conéctate como el nuevo usuario; --auth-level database indica dónde está definido:

surreal sql -e http://127.0.0.1:8000 -u app -p app_strong_password --auth-level database --ns tienda --db principal
SELECT * FROM cliente;
[[{ ciudad: 'Barcelona', id: cliente:ana, nombre: 'Ana' }]]

Paso 9: Consultar la API HTTP

Además de los SDK oficiales (JavaScript, Python, Go, Rust, Java, .NET...), cualquier cliente HTTP puede enviar SurrealQL al endpoint /sql. Las cabeceras surreal-ns y surreal-db eligen el destino, y surreal-auth-ns y surreal-auth-db indican dónde está definido el usuario:

curl -s -X POST -u app:app_strong_password \
  -H "surreal-auth-ns: tienda" -H "surreal-auth-db: principal" \
  -H "surreal-ns: tienda" -H "surreal-db: principal" \
  -H "Accept: application/json" \
  -d "SELECT nombre, precio FROM producto ORDER BY precio;" \
  http://127.0.0.1:8000/sql
[{"result":[{"nombre":"Ratón inalámbrico","precio":29.5},{"nombre":"Teclado mecánico","precio":89.9}],"status":"OK","time":"725.083µs","type":null}]

Para aplicaciones con sesión es preferible obtener un token JWT con POST /signin y enviarlo como Authorization: Bearer, o usar directamente un SDK por WebSocket, que además permite consultas en tiempo real con LIVE SELECT.

Paso 10: Exponer SurrealDB de forma segura

Mientras solo lo use una aplicación en el mismo servidor, deja SURREAL_BIND=127.0.0.1:8000. Si otras máquinas necesitan conectarse, cambia la dirección a 0.0.0.0:8000 en /etc/surrealdb.env, reinicia el servicio y abre el puerto solo a la IP de confianza (your_app_ip):

sudo systemctl restart surrealdb
sudo ufw allow from your_app_ip to any port 8000 proto tcp

Para tráfico que cruce Internet, pon delante un proxy inverso con TLS (Nginx o Caddy) o usa las opciones --web-crt y --web-key de surreal start para servir HTTPS directamente.

Paso 11: Hacer copias de seguridad

surreal export vuelca una base de datos completa (definiciones y datos) a un archivo SurrealQL, que puedes restaurar con surreal import:

surreal export -e http://127.0.0.1:8000 -u root -p your_strong_password --namespace tienda --database principal tienda-$(date +%F).surql

Para restaurar en una base de datos vacía:

surreal import -e http://127.0.0.1:8000 -u root -p your_strong_password --namespace tienda --database principal tienda-2026-09-25.surql

Programa el export con cron o un temporizador systemd y copia los archivos fuera del servidor.

Solución de problemas

  • There was a problem with authentication: el usuario está definido en otro nivel. Para usuarios ON DATABASE añade --auth-level database en surreal sql, o las cabeceras surreal-auth-ns y surreal-auth-db en HTTP.
  • El root no acepta la nueva contraseña del archivo .env: el root solo se crea en el primer arranque. Cámbiala desde SurrealQL con ALTER USER root ON ROOT PASSWORD 'nueva_password'; conectado como root.
  • Permission denied al arrancar: el directorio de datos debe pertenecer a surrealdb. Revisa con ls -ld /var/lib/surrealdb y corrige con sudo chown -R surrealdb:surrealdb /var/lib/surrealdb.

Conclusión

Tienes SurrealDB funcionando como servicio en Ubuntu 24.04, con datos persistentes en RocksDB, un usuario de aplicación con permisos limitados y consultas de documentos y de grafo probadas desde la consola y la API HTTP. Como siguientes pasos, conecta tu aplicación con el SDK oficial de tu lenguaje, define permisos por tabla con DEFINE TABLE ... PERMISSIONS y automatiza los exports diarios.