dbt (data build tool) transforma los datos que ya están en tu base de datos o data warehouse mediante consultas SQL versionadas, con dependencias entre modelos, tests de calidad y documentación generada automáticamente. En este tutorial instalarás dbt Core con el adaptador de PostgreSQL en Ubuntu 24.04, crearás un proyecto con datos de ejemplo, construirás una capa de staging y una tabla agregada, añadirás tests y generarás la documentación del linaje.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 1 GB de RAM.
  • Un usuario no root con privilegios sudo.
  • Conocimientos básicos de SQL.

En esta guía instalarás PostgreSQL en el mismo servidor para tener un entorno completo. Si ya dispones de un PostgreSQL remoto, sáltate el paso 1 y usa sus datos de conexión en el paso 4.

Paso 1: Instalar PostgreSQL y crear la base de datos

dbt no almacena datos: ejecuta SQL contra una base de datos existente. Instala PostgreSQL desde los repositorios de Ubuntu:

sudo apt update
sudo apt install -y postgresql

Crea un rol para dbt y una base de datos de la que sea propietario. Sustituye your_strong_password por una contraseña segura:

sudo -u postgres psql -c "CREATE ROLE dbt_user WITH LOGIN PASSWORD 'your_strong_password';"
sudo -u postgres createdb -O dbt_user analitica

Comprueba que puedes conectarte por TCP con ese usuario, igual que hará dbt. psql te pedirá la contraseña:

psql -h localhost -U dbt_user -d analitica -c 'SELECT current_user, current_database();'
 current_user | current_database
--------------+------------------
 dbt_user     | analitica
(1 row)

Paso 2: Instalar dbt en un entorno virtual

Instalar dbt con pip dentro de un entorno virtual evita conflictos con los paquetes de Python del sistema, que Ubuntu 24.04 protege frente a pip install global. Instala el módulo venv:

sudo apt install -y python3-venv

Crea el entorno, actívalo e instala el adaptador dbt-postgres, que trae dbt-core como dependencia:

python3 -m venv ~/dbt-venv
source ~/dbt-venv/bin/activate
pip install --upgrade pip
pip install dbt-postgres

Comprueba la instalación:

dbt --version
Core:
  - installed: 1.12.5
  - latest:    1.12.5 - Up to date!

Plugins:
  - postgres: 1.11.0 - Up to date!

Las versiones pueden ser más recientes. Recuerda ejecutar source ~/dbt-venv/bin/activate en cada sesión nueva antes de usar dbt.

Paso 3: Crear el proyecto

Inicializa un proyecto llamado tienda. La opción --skip-profile-setup evita el asistente interactivo; configurarás la conexión a mano en el paso siguiente:

cd ~
dbt init tienda --skip-profile-setup
cd ~/tienda

dbt init crea la estructura del proyecto (models/, seeds/, tests/, macros/, snapshots/, analyses/) y unos modelos de ejemplo que no necesitas. Elimínalos:

rm -rf models/example

Abre el archivo de configuración del proyecto:

nano dbt_project.yml

Al final del archivo hay un bloque models: que hace referencia a example. Sustitúyelo por este, que materializa la capa de staging como vistas y la capa de marts como tablas:

models:
  tienda:
    staging:
      +materialized: view
    marts:
      +materialized: table

Comprueba también que la línea profile: del mismo archivo vale 'tienda'. dbt usará ese nombre para buscar la conexión.

Paso 4: Configurar la conexión en profiles.yml

Las credenciales viven fuera del proyecto, en ~/.dbt/profiles.yml, para que nunca acaben en el repositorio. Crea el archivo:

mkdir -p ~/.dbt
nano ~/.dbt/profiles.yml
tienda:
  target: dev
  outputs:
    dev:
      type: postgres
      host: localhost
      port: 5432
      user: dbt_user
      password: "{{ env_var('DBT_PASSWORD') }}"
      dbname: analitica
      schema: dbt_dev
      threads: 4

La contraseña se lee de la variable de entorno DBT_PASSWORD en lugar de escribirse en claro. schema es el esquema donde dbt creará las vistas y tablas del entorno dev. Restringe los permisos del archivo y exporta la variable:

chmod 600 ~/.dbt/profiles.yml
export DBT_PASSWORD='your_strong_password'

Comprueba la configuración y la conexión:

dbt debug
...
  Connection test: [OK connection ok]

All checks passed!

Paso 5: Cargar datos de ejemplo con seeds

Los seeds son ficheros CSV pequeños que dbt carga como tablas. Sirven para datos de referencia y, en este caso, para tener datos con los que trabajar. Crea el fichero:

nano seeds/ventas_raw.csv
id_venta,cliente_email,importe,fecha_venta,estado
1,[email protected] ,120.50,2026-01-15,pagada
2,[email protected],35.00,2026-01-20,completada
3,[email protected],80.00,2026-02-03,cancelada
4,[email protected],210.99,2026-02-11,pagada
5,[email protected],15.25,2026-03-02,pendiente

Carga el seed en la base de datos:

dbt seed
...
1 of 1 OK loaded seed file dbt_dev.ventas_raw ........................ [INSERT 5 in 0.08s]
...
Completed successfully

Paso 6: Crear los modelos

Un modelo es un fichero .sql con una sentencia SELECT. dbt la envuelve en el CREATE VIEW o CREATE TABLE correspondiente y resuelve el orden de ejecución a partir de las llamadas a ref(). Crea los directorios de las dos capas:

mkdir -p models/staging models/marts

El modelo de staging limpia y normaliza los datos en bruto:

nano models/staging/stg_ventas.sql
select
    id_venta::integer                 as id_venta,
    lower(trim(cliente_email))        as email_cliente,
    importe::numeric(10, 2)           as importe,
    fecha_venta::date                 as fecha_venta,
    case
        when estado in ('completada', 'pagada') then 'completada'
        when estado = 'cancelada' then 'cancelada'
        else 'pendiente'
    end                               as estado
from {{ ref('ventas_raw') }}

El modelo de marts agrega las ventas completadas por mes a partir del modelo de staging:

nano models/marts/ventas_mensuales.sql
select
    date_trunc('month', fecha_venta)::date as mes,
    count(*)                               as num_ventas,
    sum(importe)                           as total_ventas,
    count(distinct email_cliente)          as clientes_unicos
from {{ ref('stg_ventas') }}
where estado = 'completada'
group by 1

Ejecuta los modelos:

dbt run
...
1 of 2 OK created sql view model dbt_dev.stg_ventas ................. [CREATE VIEW in 0.05s]
2 of 2 OK created sql table model dbt_dev.ventas_mensuales .......... [SELECT 2 in 0.04s]
...
Completed successfully

Consulta el resultado directamente en PostgreSQL:

psql -h localhost -U dbt_user -d analitica -c 'SELECT * FROM dbt_dev.ventas_mensuales ORDER BY mes;'
    mes     | num_ventas | total_ventas | clientes_unicos
------------+------------+--------------+-----------------
 2026-01-01 |          2 |       155.50 |               2
 2026-02-01 |          1 |       210.99 |               1
(2 rows)

Observa que el email [email protected] del seed se ha normalizado y cuenta como el mismo cliente que [email protected].

Para ejecutar solo una parte del proyecto, usa --select. Por ejemplo, dbt run --select stg_ventas+ ejecuta stg_ventas y todos los modelos que dependen de él.

Paso 7: Añadir tests de calidad de datos

Los tests genéricos se declaran en ficheros YAML junto a los modelos. Cada test es una consulta que busca filas que incumplen la regla; si encuentra alguna, falla. Crea el fichero de propiedades de la capa de staging:

nano models/staging/_staging.yml
version: 2

models:
  - name: stg_ventas
    description: "Ventas limpiadas y normalizadas a partir del seed ventas_raw."
    columns:
      - name: id_venta
        description: "Identificador único de la venta."
        data_tests:
          - unique
          - not_null
      - name: estado
        data_tests:
          - accepted_values:
              arguments:
                values: ['completada', 'cancelada', 'pendiente']
      - name: importe
        data_tests:
          - not_null

Para reglas propias, crea un test singular: una consulta en tests/ que debe devolver cero filas. Este comprueba que no hay importes negativos:

nano tests/assert_importe_no_negativo.sql
select *
from {{ ref('stg_ventas') }}
where importe < 0

Ejecuta los tests:

dbt test
...
Done. PASS=5 WARN=0 ERROR=0 SKIP=0 NO-OP=0 TOTAL=5

En el día a día conviene usar dbt build, que carga seeds, ejecuta modelos y lanza sus tests en orden de dependencias. Si un test de stg_ventas falla, los modelos que dependen de él se omiten:

dbt build

Paso 8: Crear un modelo incremental

Un modelo table se reconstruye entero en cada ejecución. Con tablas grandes es mejor un modelo incremental, que solo procesa las filas nuevas. Crea uno de ejemplo:

nano models/marts/ventas_incremental.sql
{{
  config(
    materialized='incremental',
    unique_key='id_venta'
  )
}}

select id_venta, email_cliente, importe, fecha_venta, estado
from {{ ref('stg_ventas') }}

{% if is_incremental() %}
where fecha_venta > (select max(fecha_venta) from {{ this }})
{% endif %}

La primera ejecución crea la tabla completa. En las siguientes, el bloque is_incremental() añade el filtro y dbt solo inserta o actualiza las ventas posteriores a la última cargada:

dbt run --select ventas_incremental

Si cambias la lógica del modelo y necesitas reconstruirlo desde cero, añade --full-refresh.

Paso 9: Generar y consultar la documentación

dbt genera un sitio web estático con la descripción de cada modelo, sus columnas, sus tests y el grafo de dependencias:

dbt docs generate
dbt docs serve --port 8080

El servidor escucha en 127.0.0.1. Para verlo desde tu equipo, abre en otra terminal un túnel SSH (sustituye your_user y your_server_ip):

ssh -L 8080:127.0.0.1:8080 your_user@your_server_ip

Visita http://localhost:8080 y abre la vista Lineage Graph para ver cómo ventas_raw alimenta a stg_ventas y este a los modelos de marts. Detén el servidor con Ctrl+C cuando termines.

Solución de problemas

Env var required but not provided: 'DBT_PASSWORD'. La variable no está definida en la sesión actual. Vuelve a ejecutar export DBT_PASSWORD='...' o añádela a un fichero que cargues antes de usar dbt.

Could not find profile named 'tienda'. El valor de profile: en dbt_project.yml no coincide con la clave de primer nivel de ~/.dbt/profiles.yml. Ambos deben ser idénticos.

password authentication failed for user "dbt_user". La contraseña de DBT_PASSWORD no coincide con la del rol. Cámbiala con sudo -u postgres psql -c "ALTER ROLE dbt_user PASSWORD 'your_strong_password';".

Error de SQL en un modelo. Revisa el SQL que dbt ha generado realmente con dbt compile --select nombre_modelo; el resultado se guarda en target/compiled/tienda/models/.

Conclusión

Has instalado dbt Core con PostgreSQL en Ubuntu 24.04 y has construido un proyecto con seeds, modelos en capas, tests de calidad, un modelo incremental y documentación del linaje. Como siguientes pasos, guarda el proyecto en Git, programa dbt build --target prod con un temporizador de systemd o cron añadiendo un segundo target en profiles.yml, y ejecuta dbt build en tu CI en cada pull request para detectar cambios que rompan los datos antes de desplegarlos.