SmartPot database powered by PostgreSQL 17 + TimescaleDB. It replaces the legacy document database (SmartPot-DB-Mongo) while preserving the exact data contract exposed to the rest of the platform: identifiers remain 24-character hexadecimal ObjectIds and field names seen by other components (through the API) do not change. Sensor telemetry lives in a TimescaleDB hypertable engineered for continuous high-frequency sensor ingestion.
The image includes the schema, the application role, and optional demo seed data. Only the API (SmartPot-API) connects directly to this database; for all other services, the storage layer is transparent.
Eleven tables, all using 24-character CHAR(24) identifiers generated by the API:
| Table | Contents |
|---|---|
users |
Platform accounts (unique email, BCrypt hash, role). |
crops |
Real or virtual crops. placement, device, and health stored as JSONB. |
readings |
Hypertable: sensor telemetry with one typed column per metric. |
actuators |
Outputs for each crop (water pump, UV light, fan, etc.) and their state. |
commands |
Actuator commands and their execution lifecycle. |
notifications |
PWA alerts (account-level and crop-level). |
password_reset_tokens |
SHA-256 hash of password recovery tokens. |
channel_links |
Link between an account and an external notification channel (Telegram). |
crop_channels |
Per-crop channel notification settings; recipients stored as JSONB. |
virtual_devices |
Simulation configuration for virtual crops. |
access_tokens |
Personal connection tokens (SHA-256 hash, prefix, READ or CONTROL scope). |
- Typed relational model for business entities, with
CHECKconstraints replacing the previous$jsonSchemavalidators and unique indexes (email, crop + actuator type, etc.). - JSONB for rich, optional subdocuments (placement, device, health, recipients, events), preserving document-model flexibility.
- TimescaleDB for
readings: partitioned by time into 1-day chunks (create_hypertable) with columnar compression after 12 hours and a 1-year retention policy that drops old chunks automatically. Time-series queries usetime_bucketand real-time continuous aggregates (readings_1m,readings_15m,readings_1h).
The complete schema lives in schema/schema.sql: it is the single source of truth and is fully idempotent.
- The administrator account (
POSTGRES_USER) owns the schema and is never used by the API. - The application user (
SMARTPOT_DB_USERNAME, defaultsmartpot) only has DML privileges (SELECT,INSERT,UPDATE,DELETE) on its database; it cannot alter the schema or manage roles. - An optional read-only role (
SMARTPOT_READER_USERNAME, defaultsmartpot_reader) grantsSELECTonly onreadingsandcrops(id, type, kind)forSmartPot-Trainer. - The container runs hardened (
read_only,cap_drop: ALL,no-new-privileges) just like the rest of the stack.
Copy .env.example to .env and fill in the secrets. SMARTPOT_DB_PASSWORD must be at least 16 characters long.
| Variable | Purpose |
|---|---|
POSTGRES_USER / POSTGRES_PASSWORD |
Administrator account (schema owner). |
POSTGRES_DB |
Application database (default smartpot). |
SMARTPOT_DB_USERNAME / SMARTPOT_DB_PASSWORD |
Application user (DML only). |
SMARTPOT_SEED_DEMO |
true seeds demo@smartpot.app with two crops and 48 h of readings. Local/demo use only. |
SMARTPOT_INGEST_USERNAME / SMARTPOT_INGEST_PASSWORD |
Optional async ingestion role (synchronous_commit = off). |
SMARTPOT_READER_USERNAME / SMARTPOT_READER_PASSWORD |
Optional read-only role for SmartPot-Trainer over ADBC. |
The image is public: ghcr.io/smartpottech/smartpot-db-postgre. You can launch the complete platform, including this service, with a single command and without cloning the repository:
curl -fsSL https://raw.githubusercontent.com/SmartPotTech/.github/main/docker/demo-beta/compose.yaml | docker compose -f - up -d --waitOpen http://localhost:5173 and sign in with demo@smartpot.app / SmartPot2026. Stop it with docker compose -p smartpot-demo-beta down (add -v to remove volumes). See the beta demo for details.
cp .env.example .env # fill in passwords
docker compose up -d --buildThe API connects using a standard PostgreSQL URL, for example:
postgres://smartpot:<APP_PASSWORD>@db-smartpot:5432/smartpot?sslmode=disable
The schema is idempotent. After updating the image, apply any new tables, indexes, or policies without touching existing data:
docker exec smartpot-db-postgre /opt/smartpot/migrate.shtests/validate.sh starts the hardened image and validates the schema, hypertable, retention policy, application user permissions, CHECK constraints, continuous aggregates, read-only role, idempotent migration, and demo seed data:
sh tests/validate.sh smartpot-db-postgre:ci trueThe same suite runs in CI whenever the image is built (both with and without demo seed data).
Every push to main builds and publishes the image to GHCR (ghcr.io/smartpottech/smartpot-db-postgre) and, when Docker Hub secrets are configured, mirrors it to Docker Hub. Production deployment is coordinated by the central workflow in .github.
readingshypertable with 1-day chunks, columnar compression after 12 hours (segmented bycrop_idand ordered bymeasured_at DESC), and configurable retention.- Continuous aggregates
readings_1m,readings_15m, andreadings_1hwith real-time aggregation (materialized_only = false): used by the API for time-series charts with sample-count weighted averages. - Roles:
smartpotfor the application and optionalsmartpot_readerread-only role (used by the trainer via ADBC). Thesmartpot_ingestrole is provisioned for asynchronous batch ingestion. - RLS on
cropsusingapp.current_tenant_id; tenant isolation is enforced by the API on every query and reinforced by the RLS policy.
MIT.
Base de datos de SmartPot sobre PostgreSQL 17 + TimescaleDB. Sustituye a la base documental anterior (SmartPot-DB-Mongo) manteniendo el mismo contrato de datos de cara al resto de la plataforma: los identificadores siguen siendo ObjectId hexadecimales de 24 caracteres y los nombres que ven los demás componentes (a través de la API) no cambian. La telemetría vive en una hipertabla de TimescaleDB, pensada para la ingesta continua de lecturas de sensores.
La imagen trae el esquema, el usuario de la aplicación y, opcionalmente, datos de demostración. Solo la API (SmartPot-API) se conecta a esta base; para los demás servicios la migración es transparente.
Once tablas, todas con identificadores CHAR(24) generados por la API:
| Tabla | Contenido |
|---|---|
users |
Cuentas de la plataforma (correo único, hash BCrypt, rol). |
crops |
Cultivos reales o virtuales. placement, device y health como JSONB. |
readings |
Hipertabla: telemetría de sensores. Una columna tipada por variable. |
actuators |
Salidas de cada cultivo (bomba, luz UV, ventilador, etc.) y su estado. |
commands |
Órdenes a los actuadores y su ciclo de vida. |
notifications |
Alertas de la PWA (cuenta y cultivo). |
password_reset_tokens |
Solo el SHA-256 del token de recuperación. |
channel_links |
Vínculo de una cuenta con un canal externo (Telegram). |
crop_channels |
Avisos de un cultivo por un canal; recipients como JSONB. |
virtual_devices |
Configuración de simulación de un cultivo virtual. |
access_tokens |
Tokens de conexión personal (hash SHA-256, prefijo, alcance READ/CONTROL). |
- Relacional tipado para las entidades de negocio, con restricciones
CHECKque reemplazan a los validadores$jsonSchemade la versión anterior, e índices únicos (correo, cultivo+tipo de actuador, etc.). - JSONB para los subdocumentos ricos y opcionales (lugar, dispositivo, salud, destinatarios, eventos), sin perder la flexibilidad del modelo documental.
- TimescaleDB para
readings: la tabla se particiona por tiempo en fragmentos de 1 día (create_hypertable) con compresión columnar a las 12 horas y una política de retención de 1 año que descarta los fragmentos antiguos (equivale al índice TTL anterior). Las consultas de series temporales usantime_buckety agregados continuos en tiempo real (readings_1m,readings_15m,readings_1h).
El esquema completo está en schema/schema.sql: es la única definición del modelo y es idempotente.
- La cuenta administradora (
POSTGRES_USER) es la dueña del esquema y no la usa la API. - El usuario de la aplicación (
SMARTPOT_DB_USERNAME, por defectosmartpot) solo tiene permisos de DML (SELECT,INSERT,UPDATE,DELETE) sobre su base; no puede modificar el esquema ni administrar usuarios. - El rol opcional de solo lectura (
SMARTPOT_READER_USERNAME, por defectosmartpot_reader) solo permiteSELECTsobrereadingsycrops(id, type, kind)paraSmartPot-Trainer. - La imagen corre endurecida (
read_only,cap_drop: ALL,no-new-privileges), igual que el resto del stack.
Copia .env.example a .env y complétalo. SMARTPOT_DB_PASSWORD debe tener al menos 16 caracteres.
| Variable | Uso |
|---|---|
POSTGRES_USER / POSTGRES_PASSWORD |
Cuenta administradora (dueña del esquema). |
POSTGRES_DB |
Base de la aplicación (por defecto smartpot). |
SMARTPOT_DB_USERNAME / SMARTPOT_DB_PASSWORD |
Usuario de la aplicación (solo DML). |
SMARTPOT_SEED_DEMO |
true carga demo@smartpot.app con dos cultivos y 48 h de lecturas. Solo en local o demo. |
SMARTPOT_INGEST_USERNAME / SMARTPOT_INGEST_PASSWORD |
Rol opcional de ingesta asíncrona (synchronous_commit = off). |
SMARTPOT_READER_USERNAME / SMARTPOT_READER_PASSWORD |
Rol opcional de solo lectura para SmartPot-Trainer por ADBC. |
La imagen es pública: ghcr.io/smartpottech/smartpot-db-postgre. La plataforma completa, con este servicio incluido, se levanta con un solo comando y sin descargar el código:
curl -fsSL https://raw.githubusercontent.com/SmartPotTech/.github/main/docker/demo-beta/compose.yaml | docker compose -f - up -d --waitAbre http://localhost:5173 e ingresa con demo@smartpot.app / SmartPot2026. Se detiene con docker compose -p smartpot-demo-beta down (con -v borra también los datos). Más detalle en la demo beta.
cp .env.example .env # completa las contraseñas
docker compose up -d --buildLa API se conecta con una URL de PostgreSQL, por ejemplo:
postgres://smartpot:<APP_PASSWORD>@db-smartpot:5432/smartpot?sslmode=disable
El esquema es idempotente. Tras actualizar la imagen, aplica las tablas, índices o políticas nuevas sin tocar los datos:
docker exec smartpot-db-postgre /opt/smartpot/migrate.shtests/validate.sh arranca la imagen endurecida y valida el esquema, la hipertabla, la retención, los permisos del usuario de la aplicación, las restricciones CHECK, los agregados continuos, el rol lector, la migración idempotente y los datos demo:
sh tests/validate.sh smartpot-db-postgre:ci trueLo mismo corre en CI al construir la imagen (con y sin datos demo).
Cada push a main construye y publica la imagen en GHCR (ghcr.io/smartpottech/smartpot-db-postgre) y, con los secretos de Docker Hub, su réplica. El despliegue a producción lo coordina el workflow central de .github.
- Hipertabla
readingscon chunks de 1 día, compresión columnar a las 12 horas (segmentada por cultivo y ordenada por fecha) y retención configurable. - Agregados continuos
readings_1m,readings_15myreadings_1hcon datos en tiempo real (materialized_only = false): la API los usa para las gráficas y el promedio se pondera por la cantidad de lecturas. - Roles:
smartpotpara la aplicación y, opcional,smartpot_readerde solo lectura (lo usa el entrenador por ADBC). El rolsmartpot_ingestse crea pero la API todavía no lo usa: la ingesta por lotes conCOPYcorre con el rol de la aplicación. - RLS en
cropsporapp.current_tenant_id; hoy la API no fija esa variable, así que el aislamiento entre cuentas lo garantiza la API en cada consulta.
MIT.