Com fer una auditoria tècnica de la teva instància Odoo en producció

Checklist complet per auditar el rendiment, la seguretat, les còpies de seguretat, els mòduls personalitzats, el deute tècnic i la infraestructura d'una instància Odoo en producció. Amb mètriques reals i entregable.

Per què la teva Odoo en producció necessita una auditoria periòdica

La gran majoria d'instàncies Odoo en producció a Espanya porten mesos o anys funcionant sense que ningú hagi revisat en profunditat si la configuració continua sent adequada. El sistema va responent, els usuaris treballen, i la sensació és que «funciona». Fins que no funciona.

El problema és que el deteriorament d'una instància Odoo és gradual i invisible. Les consultes que tardaven 200 ms fa dos anys ara triguen 3 segons perquè la taula account_move ha crescut de 50.000 a 800.000 registres i els índexs no s'han revisat. Les còpies de seguretat que es van configurar en l'arrencada porten sis mesos sense verificar-se i l'última còpia vàlida té quatre dies d'antiguitat. S'han instal·lat dotze mòduls de tercers sense auditoria de codi, algun amb un sudo innecessari en un controlador d'API. Els workers d'Odoo estan configurats per al servidor original de 4 nuclis i ara el servidor en té 16.

Una auditoria tècnica no és un projecte de mesos. És una revisió sistemàtica d'entre 4 i 8 hores de treball expert que produeix un informe de troballes prioritzades i un pla d'acció. Aquest article descriu exactament com fer-la.

Bloc 1: Rendiment i configuració de workers

El rendiment d'Odoo depèn de tres variables que han d'estar alineades: els recursos del servidor, la configuració de workers a odoo.conf i el comportament de la base de dades.

1.1 Verificar la configuració de workers

La fórmula de referència d'Odoo per dimensionar workers és:

# workers = (cores * 2) + 1  (recomendación general)
# max_cron_threads = 1 o 2 (nunca 0 en producción)
# limit_memory_hard <= RAM_total / (workers + 2)

# Verificar configuración actual:
cat /etc/odoo/odoo.conf | grep -E 'workers|memory|cpu|limit|thread'

# Ver workers activos en tiempo real:
ps aux | grep odoo | grep -v grep | wc -l

# Ver uso de memoria por proceso:
ps aux --sort=-%mem | grep odoo | head -20

En una instància amb 8 nuclis i 32 GB de RAM, la configuració correcta és aproximadament: workers = 16, limit_memory_hard = 2684354560 (2,5 GB), limit_memory_soft = 2147483648 (2 GB). Si el servidor té aquests recursos i workers = 4 (la configuració per defecte de moltes instal·lacions), s'està deixant un 75% de la capacitat del servidor sense usar.

1.2 Detectar consultes lentes a PostgreSQL

Activa log_min_duration_statement si no està ja actiu i analitza amb pg_stat_statements:

-- Verificar si pg_stat_statements está activo:
SELECT * FROM pg_extension WHERE extname = 'pg_stat_statements';

-- Si no está, activar (requiere reinicio de PG):
-- shared_preload_libraries = 'pg_stat_statements'  en postgresql.conf
CREATE EXTENSION pg_stat_statements;

-- Top 15 queries más lentas (tiempo total acumulado):
SELECT
    LEFT(query, 120)               AS query_preview,
    calls,
    ROUND(total_exec_time::numeric, 2) AS total_ms,
    ROUND(mean_exec_time::numeric, 2)  AS mean_ms,
    ROUND(stddev_exec_time::numeric, 2) AS stddev_ms,
    rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 15;

-- Top queries por llamadas (candidates para optimizar con índices):
SELECT
    LEFT(query, 120) AS query_preview,
    calls,
    ROUND(mean_exec_time::numeric, 2) AS mean_ms
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 15;

1.3 Índexs faltants i taules inflades

-- Tablas más grandes (candidatas a revisar vacuum/autovacuum):
SELECT
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
    pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) AS table_size,
    n_dead_tup,
    n_live_tup,
    ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_pct,
    last_autovacuum,
    last_autoanalyze
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 20;

-- Índices no usados (candidatos a eliminar):
SELECT
    schemaname,
    tablename,
    indexname,
    idx_scan,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
  AND schemaname = 'public'
ORDER BY pg_relation_size(indexrelid) DESC
LIMIT 20;

Bloc 2: Seguretat

La seguretat d'una instància Odoo en producció té quatre capes a auditar: el servidor, Nginx, la configuració d'Odoo i els mòduls personalitzats instal·lats.

2.1 Checklist de seguretat del servidor

  • El port 8069 (Odoo) és accessible des d'Internet directament? Ha d'estar bloquejat pel firewall; només Nginx ha de poder connectar-s'hi.
  • El port 5432 (PostgreSQL) és accessible des d'Internet? Ha d'estar tancat. Només el servidor d'aplicació ha de poder connectar.
  • Estan actualitzats els paquets del sistema? apt list --upgradable 2>/dev/null | wc -l — si hi ha més de 20 paquets pendents d'actualitzar, revisar.
  • Hi ha usuaris amb claus SSH per defecte o contrasenyes febles? Audita /etc/ssh/sshd_config: PasswordAuthentication no ha d'estar actiu.
  • Està configurat fail2ban per al port SSH i per a Nginx? Les IPs que fan força bruta han de ser prohibides automàticament.

2.2 Checklist de seguretat d'Odoo

# Verificar que el master password de Odoo está configurado y es robusto:
grep admin_passwd /etc/odoo/odoo.conf
# Si está vacío o es "admin", CRÍTICO — cualquiera puede acceder a /web/database/manager

# Verificar que el gestor de BBDD está bloqueado o deshabilitado:
curl -s https://tuodoo.com/web/database/manager | grep -c "Database Manager"
# Si devuelve 1, el gestor está expuesto. Añadir: list_db = False en odoo.conf

# Verificar headers de seguridad en Nginx:
curl -sI https://tuodoo.com | grep -E 'X-Frame|X-Content|Strict|Content-Security'

2.3 Auditoria ràpida de mòduls personalitzats: patrons de risc

Els mòduls personalitzats són la superfície d'atac més habitual en instàncies Odoo madures. Revisa amb grep:

# Buscar uso de sudo() sin justificación en controladores de API:
grep -r 'sudo()' /opt/odoo/custom_addons/ --include='*.py' -l

# Buscar queries SQL construidas por concatenación (riesgo SQLi):
grep -rn "cr.execute.*%s" /opt/odoo/custom_addons/ --include='*.py' | grep -v 'params'

# Buscar eval() o exec() (riesgo de ejecución de código arbitrario):
grep -rn 'eval(\|exec(' /opt/odoo/custom_addons/ --include='*.py'

# Buscar rutas HTTP sin autenticación declarada:
grep -rn "auth.*public\|auth.*none" /opt/odoo/custom_addons/ --include='*.py'

Bloc 3: Còpies de seguretat

En la meitat de les auditories que fem, la còpia de seguretat «funciona» en teoria però ningú l'ha restaurada mai per verificar-la. Una còpia de seguretat no verificada no és una còpia: és una esperança.

3.1 Verificar que les còpies existeixen i són recents

# Verificar backups de PostgreSQL (pg_dump o pgBackRest):
ls -lhrt /var/backups/odoo/ | tail -10
# ¿Cuándo fue el último? ¿Cuántos días de antigüedad tiene?

# Si se usa el backup nativo de Odoo (zip de BBDD + filestore):
ls -lhrt /var/lib/odoo/backups/ | tail -10

# Tamaño del backup vs. tamaño de la BBDD (deben ser comparables):
du -sh /var/backups/odoo/latest_backup.zip
psql -U odoo -c "SELECT pg_size_pretty(pg_database_size('odoo_prod'));"

3.2 Prova de restauració (el pas que ningú fa)

# Restaurar el último backup en una BBDD temporal para verificar integridad:
pg_restore -U postgres -d odoo_test_restore \
  --no-owner --no-acl \
  /var/backups/odoo/backup_$(date +%Y%m%d).dump

# Verificar que la restauración tiene las tablas esperadas:
psql -U odoo -d odoo_test_restore -c "
SELECT COUNT(*) FROM sale_order;
SELECT COUNT(*) FROM account_move;
SELECT MAX(write_date) FROM res_partner;
"
# Si los conteos son cero o la fecha es antigua, el backup está corrupto

Bloc 4: Mòduls personalitzats vs. core — deute tècnic

Un dels majors riscos a llarg termini d'una instància Odoo és l'acumulació de mòduls personalitzats que sobreescriuen el comportament del core sense documentació ni tests. Cada actualització de versió d'Odoo es converteix en un projecte de mesos si hi ha desenes de mòduls personalitzats amb overrides extensos.

4.1 Inventari de mòduls instal·lats

-- Módulos instalados que NO son de Odoo ni de OCA:
SELECT
    name,
    author,
    installed_version,
    state,
    category
FROM ir_module_module
WHERE state = 'installed'
  AND author NOT ILIKE '%Odoo%'
  AND author NOT ILIKE '%OCA%'
ORDER BY name;

-- Módulos con muchos modelos heredados (señal de deuda técnica alta):
SELECT
    module,
    COUNT(*) AS inherited_models
FROM ir_model_inherit
GROUP BY module
ORDER BY inherited_models DESC
LIMIT 20;

4.2 Classificar el deute tècnic

Tipus de deuteSenyalRisc
Overrides de mètodes core sense herència correctadef write(self): sense super()Alt — trenca en actualitzacions
Vistes heretades amb xpath fràgil//field[@name='x'] en vistes que canvien entre versionsMitjà
Dades de configuració en codi PythonIDs hardcodejats en codi (env.ref('module.record_id'))Mitjà — depèn de l'entorn
Mòduls sense testsDirectori tests/ buit o absentMitjà — risc silenciós en actualitzacions
Dependències de mòduls de tercers sense mantenimentDarrer commit fa >2 anys a GitHubAlt per a migracions

Bloc 5: Infraestructura i sistema operatiu

  • Versió del sistema operatiu: Ubuntu 22.04 LTS o Debian 12 són les plataformes recomanades el 2026. Si el servidor funciona amb Ubuntu 18.04 o 20.04, el cicle de suport s'està acabant o ja ha acabat.
  • Versió de PostgreSQL: Odoo 16/17 amb PostgreSQL 14 o 15 és correcte. PostgreSQL 12 o anterior té un query planner menys eficient i extensions de seguretat desactualitzades.
  • Espai en disc: verifica que hi ha almenys un 30% d'espai lliure. Un disc ple en un servidor Odoo en producció provoca corrupció de dades.
  • Swap: ha d'existir i ser almenys igual a la RAM. Si el OOM killer està actiu als logs, la configuració de memòria d'Odoo (limit_memory_hard) està mal dimensionada.
# Verificaciones de infraestructura en un comando:
echo "=== DISCO ==="
df -h | grep -v tmpfs
echo "=== MEMORIA ==="
free -h
echo "=== SWAP ==="
swapon --show
echo "=== OOM KILLS (últimas 48h) ==="
dmesg --since -48h | grep -i 'oom\|killed process' | tail -10
echo "=== VERSIÓN PG ==="
psql --version
echo "=== UPTIME ==="
uptime
echo "=== CARGA SISTEMA ==="
sar -u 1 3 2>/dev/null || top -bn1 | head -5

Bloc 6: Observabilitat — quines mètriques mirar

Una instància Odoo sense observabilitat és un sistema que només avisa quan ja està trencat. Les mètriques mínimes que han d'estar monitoritzades són:

MètricaLlindar d'alertaCom mesurar-la
TTFB (Time To First Byte) d'Odoo> 2 s en rutes crítiquesBlackbox exporter / curl + temps
Workers actius vs. disponibles> 80% de workers ocupatsEndpoint /web/health + mètriques Nginx
Connexions PostgreSQL actives> 80% de max_connectionsSELECT count(*) FROM pg_stat_activity
Lag de replicació (si hi ha rèplica)> 30 segonspg_stat_replication
Consultes bloquejades (locks)Qualsevol consulta > 5 min bloquejadapg_stat_activity WHERE wait_event_type='Lock'
Espai en disc> 70% ocupatnode_exporter / df
Èxit de les còpies de seguretatMés de 24 h sense còpia vàlidaScript de verificació + alerta Telegram

L'eina més ràpida de desplegar per a Odoo és l'stack ELK (Elasticsearch, Logstash, Kibana) amb Filebeat recollint els logs d'Odoo, combinat amb alertes via bot de Telegram. Amb aquesta configuració, qualsevol error 500, consulta lenta o fallada de worker genera un missatge al mòbil del responsable tècnic en temps real.

L'entregable d'una auditoria tècnica

Una auditoria tècnica professional no acaba en una llista de troballes sense prioritzar. L'entregable ha de tenir aquesta estructura:

  1. Resum executiu (1 pàgina): els 3–5 riscos crítics que requereixen acció immediata, en llenguatge no tècnic per a la direcció.
  2. Troballes per bloc: rendiment, seguretat, còpies de seguretat, codi, infraestructura, observabilitat. Cada troballa amb severitat (crític / alt / mitjà / baix), evidència concreta i recomanació específica.
  3. Pla d'acció prioritzat: què fer primer, què pot esperar, quin és el deute tècnic a gestionar a mig termini.
  4. Estimació d'esforç: hores aproximades per resoldre cada troballa, perquè el client pugui planificar i pressupostar.
  5. Baseline de mètriques: còpia de les mètriques clau en el moment de l'auditoria (consultes lentes, espai en disc, configuració de workers) per comparar en futures revisions.

Amb quina freqüència fer l'auditoria

La recomanació general depèn de l'ús del sistema:

  • Instància crítica (> 50 usuaris, operacions en temps real): revisió trimestral de mètriques + auditoria completa anual.
  • Instància mitjana (10–50 usuaris, operativa d'oficina): auditoria completa cada 6 mesos.
  • Instància petita (< 10 usuaris): auditoria anual, més sempre abans d'una actualització de versió d'Odoo.
  • Després de qualsevol incidència greu (caiguda, corrupció de dades, incident de seguretat): auditoria immediata.

El que una auditoria no pot fer sola

Una auditoria és una fotografia de l'estat actual. Sense un pla de remediació executat, les troballes s'acumulen en un document que ningú torna a llegir. El valor real està en la combinació de diagnòstic + pla d'acció + execució. Per a instàncies crítiques, la recomanació és contractar un retainer tècnic mensual que inclogui la resolució proactiva de les troballes recurrents: optimització de consultes, gestió d'actualitzacions, verificació de còpies de seguretat i monitorització contínua.

Vols que revisem el teu Odoo en producció?

Sol·licitar auditoria tècnica gratuïta

Pipeline CI/CD complet per a Odoo amb GitHub Actions i Docker: guia completa
Arquitectura real d'un pipeline d'integració i desplegament continu per a mòduls Odoo en producció