Files
2026-08-28 10:11:11 -03:00

2398 lines
101 KiB
HTML

<!DOCTYPE html>
<html dir="rtl" lang="ar">
<head>
<link rel="preconnect" href="https://d3js.org" crossorigin>
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="alternate icon" href="/favicon.ico">
<title>Odoo Docker Hosting 2026 | Odoo Expertos · AR</title>
<meta name="description" content="Guia definitiva de Odoo con Docker en 2026. Aprende a desplegar Odoo en contenedores: Docker Compose, configuracion, optimizacion, ba - Odoo Docker Hosting 2026 | AR">
<meta name="keywords" content="odoo docker, docker compose odoo, contenedores odoo, odoo docker hosting, desplegar odoo docker, odoo containerizado, docker postgresql odoo, odoo docker produccion">
<meta name="author" content="د. ماريا إيلينا رودريغيز">
<meta name="robots" content="index, follow">
<meta name="language" content="ar">
<link rel="canonical" href="https://www.odoo-expertos.com/ar/odoo-hosting/odoo-docker-hosting-2026/">
<!-- Hreflang -->
<link rel="alternate" hreflang="ar" href="https://www.odoo-expertos.com/ar/odoo-hosting/odoo-docker-hosting-2026/" />
<link rel="alternate" hreflang="en" href="https://www.odoo-expertos.com/en/odoo-hosting/odoo-docker-hosting-2026/" />
<link rel="alternate" hreflang="de" href="https://www.odoo-expertos.com/de/odoo-hosting/odoo-docker-hosting-2026/" />
<link rel="alternate" hreflang="fr" href="https://www.odoo-expertos.com/fr/odoo-hosting/odoo-docker-hosting-2026/" />
<link rel="alternate" hreflang="x-default" href="https://www.odoo-expertos.com/odoo-hosting/odoo-docker-hosting-2026/" />
<!-- Open Graph -->
<meta property="og:title" content="Odoo Docker Hosting 2026: Guia Completa de Contenedores">
<meta property="og:description" content="Guia definitiva de Odoo con Docker en 2026. Aprende a desplegar Odoo en contenedores con Docker Compose, optimizacion y mejores practicas.">
<meta property="og:type" content="article">
<meta property="og:url" content="https://www.odoo-expertos.com/ar/odoo-hosting/odoo-docker-hosting-2026/">
<meta property="og:image" content="https://www.odoo-expertos.com/images/odoo-docker-hosting-2026.jpg">
<meta property="og:site_name" content="Odoo Expertos">
<meta property="og:locale" content="ar_SA">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Odoo Docker Hosting 2026: Guia Completa de Contenedores">
<meta name="twitter:description" content="Guia definitiva de Odoo con Docker. Docker Compose, optimizacion, backups y mejores proveedores.">
<!-- Schema.org Article -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "Odoo Docker Hosting 2026: Guia Completa de Contenedores",
"description": "Guia definitiva de Odoo con Docker en 2026. Aprende a desplegar Odoo en contenedores: Docker Compose, configuracion, optimizacion, backups y mejores proveedores de hosting Docker.",
"author": {
"@type": "Person",
"name": "د. ماريا إيلينا رودريغيز",
"url": "https://www.odoo-expertos.com/autor/maria-elena-rodriguez/",
"jobTitle": "Consultora Senior en نعمstemas ERP"
},
"publisher": {
"@type": "Organization",
"name": "Odoo Expertos",
"logo": {
"@type": "ImageObject",
"url": "https://www.odoo-expertos.com/logo.png"
}
},
"datePublished": "2026-01-25",
"dateModified": "2026-01-25",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://www.odoo-expertos.com/odoo-hosting/odoo-docker-hosting-2026/"
},
"proficiencyLevel": "Intermediate",
"dependencies": "Docker, Docker Compose, PostgreSQL"
}
</script>
<!-- FAQ Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Es Docker recomendable para Odoo en produccion?",
"acceptedAnswer": {
"@type": "Answer",
"text": "نعم, Docker es altamente recomendable para Odoo en produccion en 2026. Ofrece aislamiento, reproducibilidad, escalabilidad horizontal y facilita los despliegues. Empresas como Amazon, Google y Microsoft utilizan contenedores en produccion. La clave esta en una correcta configuracion de volumenes persistentes, redes y orquestacion."
}
},
{
"@type": "Question",
"name": "Cuantos recursos necesita Odoo en Docker?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Para Odoo 18 en Docker se recomienda: 4GB RAM minimo (8GB recomendado), 2 CPU cores (4 recomendado), 40GB SSD para volumenes. Docker anade aproximadamente un 2-5% de overhead comparado con instalacion nativa, pero los beneficios de gestion superan ampliamente este coste."
}
},
{
"@type": "Question",
"name": "Como hago backup de Odoo en Docker?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Para backup de Odoo en Docker: 1) Backup de PostgreSQL con pg_dump desde el contenedor, 2) Backup del volumen filestore, 3) Backup de configuracion odoo.conf. Automatiza con cron jobs y docker exec. Recomendamos backups incrementales diarios y completos semanales con retencion de 30 dias."
}
},
{
"@type": "Question",
"name": "Puedo usar Docker Compose para multiples instancias de Odoo?",
"acceptedAnswer": {
"@type": "Answer",
"text": "نعم, Docker Compose es ideal para multi-tenant Odoo. Puedes definir multiples servicios Odoo en el mismo archivo compose, cada uno con su propia base de datos. Usa Traefik o Nginx como reverse proxy para enrutar dominios a cada instancia. Esto es perfecto para agencias que gestionan varios clientes."
}
},
{
"@type": "Question",
"name": "Docker Swarm o Kubernetes para Odoo?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Para la mayoria de despliegues Odoo, Docker Swarm es suficiente y mas simple de gestionar. Kubernetes es recomendable solo para despliegues muy grandes (100+ usuarios concurrentes) o si ya tienes infraestructura K8s. Docker Swarm ofrece alta disponibilidad, balanceo de carga y escalado con menor complejidad operativa."
}
},
{
"@type": "Question",
"name": "Como actualizo Odoo en Docker sin downtime?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Para actualizar Odoo en Docker con minimo downtime: 1) Pull de la nueva imagen, 2) Backup completo, 3) Levantar nuevo contenedor en paralelo, 4) Ejecutar migracion de base de datos, 5) Cambiar el proxy al nuevo contenedor, 6) Eliminar contenedor antiguo. Con Docker Swarm puedes hacer rolling updates automaticos."
}
},
{
"@type": "Question",
"name": "Cual es la mejor imagen Docker para Odoo?",
"acceptedAnswer": {
"@type": "Answer",
"text": "La imagen oficial de Odoo (odoo:18) es la recomendada para produccion. Esta optimizada, actualizada regularmente y soportada. Para personalizaciones, crea tu propia imagen basada en la oficial con un Dockerfile. Evita imagenes de terceros no verificadas por razones de seguridad."
}
},
{
"@type": "Question",
"name": "Como configuro SSL/HTTPS para Odoo en Docker?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Para SSL en Odoo Docker usa un reverse proxy: 1) Traefik con Let's Encrypt automatico (recomendado), 2) Nginx con Certbot, 3) Caddy con SSL automatico. El proxy maneja SSL y redirige al contenedor Odoo via HTTP interno. Nunca expongas Odoo directamente a internet sin SSL."
}
}
]
}
</script>
<!-- HowTo Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "Como desplegar Odoo con Docker Compose",
"description": "Guia paso a paso para desplegar Odoo 18 en produccion usando Docker y Docker Compose",
"totalTime": "PT30M",
"estimatedCost": {
"@type": "MonetaryAmount",
"currency": "EUR",
"value": "0"
},
"tool": [
{
"@type": "HowToTool",
"name": "Docker Engine"
},
{
"@type": "HowToTool",
"name": "Docker Compose"
}
],
"step": [
{
"@type": "HowToStep",
"name": "Instalar Docker",
"text": "Instala Docker Engine y Docker Compose en tu servidor Ubuntu 24.04"
},
{
"@type": "HowToStep",
"name": "Crear docker-compose.yml",
"text": "Crea el archivo docker-compose.yml con los servicios de Odoo y PostgreSQL"
},
{
"@type": "HowToStep",
"name": "Configurar volumenes",
"text": "Define volumenes persistentes para datos de PostgreSQL y filestore de Odoo"
},
{
"@type": "HowToStep",
"name": "Iniciar contenedores",
"text": "Ejecuta docker compose up -d para iniciar los servicios"
},
{
"@type": "HowToStep",
"name": "Configurar reverse proxy",
"text": "Configura Traefik o Nginx como reverse proxy con SSL"
}
]
}
</script>
<!-- BreadcrumbList Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "الرئيسية",
"item": "https://www.odoo-expertos.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "استضافة Odoo",
"item": "https://www.odoo-expertos.com/ar/odoo-hosting/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Odoo Docker Hosting 2026",
"item": "https://www.odoo-expertos.com/ar/odoo-hosting/odoo-docker-hosting-2026/"
}
]
}
</script>
<!-- Product Comparison Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": "Mejores Proveedores de Docker Hosting para Odoo 2026",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": {
"@type": "Product",
"name": "DigitalOcean App Platform",
"description": "Plataforma de contenedores gestionada ideal para Odoo Docker",
"offers": {
"@type": "Offer",
"price": "25",
"priceCurrency": "EUR"
}
}
},
{
"@type": "ListItem",
"position": 2,
"item": {
"@type": "Product",
"name": "Hetzner Cloud",
"description": "VPS de alto rendimiento con Docker preinstalado",
"offers": {
"@type": "Offer",
"price": "15",
"priceCurrency": "EUR"
}
}
},
{
"@type": "ListItem",
"position": 3,
"item": {
"@type": "Product",
"name": "AWS ECS/Fargate",
"description": "Orquestacion de contenedores empresarial",
"offers": {
"@type": "Offer",
"price": "50",
"priceCurrency": "EUR"
}
}
}
]
}
</script>
<link rel="stylesheet" href="/css/main.min.css">
<style>
.pillar-hero {
background: linear-gradient(135deg, #1a1a1a 0%, #0d47a1 50%, #1a1a1a 100%);
padding: 4rem 2rem;
text-align: center;
border-bottom: 3px solid #2196F3;
position: relative;
overflow: hidden;
}
.pillar-hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(ellipse at center, rgba(33,150,243,0.15) 0%, transparent 70%);
pointer-events: none;
}
.pillar-hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
color: #2196F3;
margin-bottom: 1rem;
line-height: 1.2;
position: relative;
}
.pillar-subtitle {
font-size: 1.3rem;
color: #b0b0b0;
max-width: 800px;
margin: 0 auto 2rem;
}
.pillar-meta {
display: flex;
justify-content: center;
gap: 2rem;
flex-wrap: wrap;
font-size: 0.9rem;
color: #888;
}
.pillar-meta span {
display: flex;
align-items: center;
gap: 0.5rem;
}
.quick-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1.5rem;
padding: 2rem;
background: #2a2a2a;
border-radius: 12px;
margin: 2rem 0;
}
.stat-card {
text-align: center;
padding: 1.5rem;
background: rgba(33,150,243,0.1);
border-radius: 8px;
border: 1px solid rgba(33,150,243,0.2);
transition: transform 0.3s ease, border-color 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
border-color: #2196F3;
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: #2196F3;
display: block;
}
.stat-label {
color: #b0b0b0;
font-size: 0.9rem;
margin-top: 0.5rem;
}
.toc {
background: #2a2a2a;
border: 2px solid #2196F3;
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
}
.toc h2 {
color: #2196F3;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.toc-list {
list-style: none;
padding: 0;
columns: 2;
column-gap: 2rem;
}
.toc-list li {
margin-bottom: 0.75rem;
break-inside: avoid;
}
.toc-list a {
color: #e0e0e0;
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
transition: color 0.3s ease;
}
.toc-list a:hover {
color: #2196F3;
}
.toc-list a::before {
content: '>';
color: #2196F3;
font-family: monospace;
}
.content-section {
margin: 3rem 0;
}
.content-section h2 {
color: #64B5F6;
margin-bottom: 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid rgba(33,150,243,0.3);
}
.content-section h3 {
color: #90CAF9;
margin: 2rem 0 1rem;
}
.chart-section {
margin: 3rem 0;
padding: 2rem;
background: #2a2a2a;
border-radius: 12px;
}
.chart-section h3 {
color: #64B5F6;
margin-bottom: 1rem;
}
.chart-container {
width: 100%;
min-height: 400px;
position: relative;
}
.code-block {
background: #1a1a2e;
border-radius: 8px;
padding: 1.5rem;
margin: 1.5rem 0;
overflow-x: auto;
border: 1px solid #333;
position: relative;
}
.code-block::before {
content: attr(data-lang);
position: absolute;
top: 0;
right: 0;
background: #2196F3;
color: #fff;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
border-radius: 0 8px 0 8px;
text-transform: uppercase;
}
.code-block pre {
margin: 0;
color: #e0e0e0;
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 0.9rem;
line-height: 1.6;
white-space: pre;
}
.code-block .comment { color: #6a9955; }
.code-block .keyword { color: #569cd6; }
.code-block .string { color: #ce9178; }
.code-block .number { color: #b5cea8; }
.code-block .property { color: #9cdcfe; }
.comparison-table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
background: #2a2a2a;
border-radius: 12px;
overflow: hidden;
}
.comparison-table th,
.comparison-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #3a3a3a;
}
.comparison-table th {
background: rgba(33,150,243,0.2);
color: #2196F3;
font-weight: 600;
}
.comparison-table tr:hover {
background: rgba(33,150,243,0.05);
}
.comparison-table .check {
color: #4CAF50;
font-weight: bold;
}
.comparison-table .cross {
color: #f44336;
}
.provider-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
margin: 2rem 0;
}
.provider-card {
background: #2a2a2a;
border-radius: 12px;
padding: 1.5rem;
border: 1px solid rgba(33,150,243,0.2);
transition: transform 0.3s ease, border-color 0.3s ease;
}
.provider-card:hover {
transform: translateY(-5px);
border-color: #2196F3;
}
.provider-card.featured {
border-color: #2196F3;
background: linear-gradient(135deg, rgba(33,150,243,0.15) 0%, #2a2a2a 100%);
position: relative;
}
.provider-card .badge {
display: inline-block;
padding: 0.25rem 0.75rem;
background: #2196F3;
color: #fff;
border-radius: 20px;
font-size: 0.8rem;
margin-bottom: 1rem;
}
.provider-card h4 {
color: #fff;
margin-bottom: 0.5rem;
font-size: 1.3rem;
}
.provider-card .price {
font-size: 2rem;
color: #2196F3;
font-weight: 700;
}
.provider-card .price-period {
color: #888;
font-size: 0.9rem;
}
.provider-card ul {
list-style: none;
padding: 0;
margin: 1rem 0;
}
.provider-card li {
padding: 0.5rem 0;
border-bottom: 1px solid #3a3a3a;
display: flex;
align-items: center;
gap: 0.5rem;
color: #b0b0b0;
}
.provider-card li::before {
content: '>';
color: #4CAF50;
font-weight: bold;
font-family: monospace;
}
.provider-card .cta-btn {
display: block;
text-align: center;
padding: 0.75rem 1.5rem;
background: #2196F3;
color: #fff;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.3s ease, transform 0.3s ease;
margin-top: 1rem;
}
.provider-card .cta-btn:hover {
background: #64B5F6;
transform: scale(1.02);
}
.faq-section {
margin: 3rem 0;
}
.faq-item {
background: #2a2a2a;
border-radius: 8px;
margin-bottom: 1rem;
overflow: hidden;
}
.faq-question {
padding: 1.25rem;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
font-weight: 500;
transition: background 0.3s ease;
}
.faq-question:hover {
background: rgba(33,150,243,0.1);
}
.faq-question::after {
content: '+';
font-size: 1.5rem;
color: #2196F3;
transition: transform 0.3s ease;
}
.faq-item.open .faq-question::after {
transform: rotate(45deg);
}
.faq-answer {
padding: 0 1.25rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease, padding 0.3s ease;
color: #b0b0b0;
}
.faq-item.open .faq-answer {
padding: 0 1.25rem 1.25rem;
max-height: 500px;
}
.cta-banner {
background: linear-gradient(135deg, rgba(33,150,243,0.2) 0%, rgba(100,181,246,0.1) 100%);
border: 2px solid rgba(33,150,243,0.5);
border-radius: 12px;
padding: 2.5rem;
text-align: center;
margin: 3rem 0;
}
.cta-banner h3 {
color: #2196F3;
margin-bottom: 1rem;
font-size: 1.8rem;
}
.cta-banner p {
margin-bottom: 1.5rem;
color: #b0b0b0;
font-size: 1.1rem;
}
.cta-banner .cta-btn {
display: inline-block;
padding: 1rem 2.5rem;
background: #2196F3;
color: #fff;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
transition: background 0.3s ease, transform 0.3s ease;
}
.cta-banner .cta-btn:hover {
background: #64B5F6;
transform: scale(1.05);
}
.related-articles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin: 2rem 0;
}
.related-card {
background: #2a2a2a;
border-radius: 8px;
overflow: hidden;
transition: transform 0.3s ease;
text-decoration: none;
}
.related-card:hover {
transform: translateY(-5px);
}
.related-card-image {
height: 150px;
background: linear-gradient(135deg, rgba(33,150,243,0.3), rgba(100,181,246,0.3));
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
}
.related-card-content {
padding: 1.25rem;
}
.related-card h4 {
color: #fff;
margin-bottom: 0.5rem;
font-size: 1rem;
}
.related-card p {
color: #888;
font-size: 0.9rem;
margin: 0;
}
.d3-tooltip {
position: absolute;
background: rgba(0,0,0,0.95);
color: #fff;
padding: 0.75rem 1rem;
border-radius: 8px;
font-size: 0.9rem;
pointer-events: none;
z-index: 100;
max-width: 280px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
border: 1px solid rgba(33,150,243,0.3);
}
.d3-tooltip strong {
color: #2196F3;
}
.highlight-box {
background: linear-gradient(135deg, rgba(33,150,243,0.1) 0%, rgba(100,181,246,0.05) 100%);
border-left: 4px solid #2196F3;
padding: 1.5rem;
margin: 2rem 0;
border-radius: 0 8px 8px 0;
}
.highlight-box h4 {
color: #2196F3;
margin-bottom: 0.5rem;
}
.warning-box {
background: linear-gradient(135deg, rgba(255,152,0,0.1) 0%, rgba(255,193,7,0.05) 100%);
border-left: 4px solid #FF9800;
padding: 1.5rem;
margin: 2rem 0;
border-radius: 0 8px 8px 0;
}
.warning-box h4 {
color: #FF9800;
margin-bottom: 0.5rem;
}
.pros-cons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin: 1.5rem 0;
}
.pros, .cons {
padding: 1.5rem;
border-radius: 8px;
}
.pros {
background: rgba(76, 175, 80, 0.1);
border: 1px solid rgba(76, 175, 80, 0.3);
}
.cons {
background: rgba(244, 67, 54, 0.1);
border: 1px solid rgba(244, 67, 54, 0.3);
}
.pros h4 {
color: #4CAF50;
margin-bottom: 1rem;
}
.cons h4 {
color: #f44336;
margin-bottom: 1rem;
}
.pros ul, .cons ul {
list-style: none;
padding: 0;
margin: 0;
}
.pros li::before {
content: '> ';
color: #4CAF50;
}
.cons li::before {
content: 'x ';
color: #f44336;
}
.architecture-diagram {
background: #1a1a2e;
border-radius: 12px;
padding: 2rem;
margin: 2rem 0;
border: 1px solid #333;
}
.step-list {
counter-reset: step-counter;
list-style: none;
padding: 0;
}
.step-list li {
counter-increment: step-counter;
padding: 1rem 0 1rem 3.5rem;
position: relative;
border-left: 2px solid #333;
margin-left: 1rem;
}
.step-list li::before {
content: counter(step-counter);
position: absolute;
left: -1.25rem;
top: 1rem;
width: 2.5rem;
height: 2.5rem;
background: #2196F3;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.step-list li:last-child {
border-left: 2px solid transparent;
}
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
@media (max-width: 768px) {
.toc-list {
columns: 1;
}
.pillar-meta {
flex-direction: column;
gap: 0.5rem;
}
.chart-container {
min-height: 300px;
}
.pros-cons {
grid-template-columns: 1fr;
}
.provider-grid {
grid-template-columns: 1fr;
}
.code-block {
font-size: 0.8rem;
}
}
</style>
</head>
<body>
<div id="header"><header class="site-header"><nav class="navbar"><div class="container"><div class="nav-brand"><a href="/ar/" class="logo"><span class="logo-text">Odoo Expertos</span></a></div><button class="nav-toggle" aria-label="Toggle navigation"><span></span><span></span><span></span></button><div class="nav-menu"><ul class="nav-list"><li><a href="/ar/">Inicio</a></li><li><a href="/ar/odoo/">Odoo</a></li><li><a href="/ar/odoo-ia/">Odoo AI</a></li><li><a href="/ar/odoo-hosting/">Odoo Hosting</a></li><li><a href="/ar/mapa-del-sitio/">Sitemap</a></li></ul></div></div></nav></header></div><button class="nav-toggle" aria-label="Toggle navigation"><span></span><span></span><span></span></button><div class="nav-menu"><ul class="nav-list"><li><a href="/ar/">Inicio</a></li><li><a href="/ar/odoo/">Odoo</a></li><li><a href="/ar/odoo-ia/">Odoo AI</a></li><li><a href="/ar/odoo-hosting/">Odoo Hosting</a></li><li><a href="/ar/mapa-del-sitio/">Sitemap</a></li></ul></div></div></nav></header></div>
<!-- Breadcrumbs -->
<nav class="breadcrumbs">
<div class="container">
<ol class="breadcrumb-list" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/"><span itemprop="name">الرئيسية</span></a>
<span itemprop="position" style="display:none">1</span>
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/ar/odoo-hosting/"><span itemprop="name">استضافة Odoo</span></a>
<span itemprop="position" style="display:none">2</span>
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<span itemprop="name">Odoo Docker Hosting 2026</span>
<span itemprop="position" style="display:none">3</span>
</li>
</ol>
</div>
</nav>
<!-- Hero Section -->
<section class="pillar-hero">
<div class="container">
<h1>Odoo Docker Hosting 2026: Guia Completa de Contenedores</h1>
<p class="pillar-subtitle">Domina el despliegue de Odoo con Docker. Aprende Docker Compose, optimizacion de rendimiento, estrategias de backup y los mejores proveedores de hosting para contenedores.</p>
<div class="pillar-meta">
<span>منشور: 25 enero 2026</span>
<span>محدث: 25 enero 2026</span>
<span>30 دقيقة قراءة</span>
<span>د. ماريا إيلينا رودريغيز</span>
</div>
</div>
</section>
<main class="container">
<!-- Quick Stats -->
<section class="quick-stats fade-in">
<div class="stat-card">
<span class="stat-number">85%</span>
<span class="stat-label">Empresas usan Docker</span>
</div>
<div class="stat-card">
<span class="stat-number">70%</span>
<span class="stat-label">Reduccion tiempo deploy</span>
</div>
<div class="stat-card">
<span class="stat-number">15EUR</span>
<span class="stat-label">Precio minimo/mes</span>
</div>
<div class="stat-card">
<span class="stat-number">99.9%</span>
<span class="stat-label">Uptime con orquestacion</span>
</div>
<div class="stat-card">
<span class="stat-number">5min</span>
<span class="stat-label">Tiempo de despliegue</span>
</div>
</section>
<!-- Table of Contents -->
<nav class="toc fade-in">
<h2>محتوى هذا الدليل</h2>
<ol class="toc-list">
<li><a href="#introduccion">Por que Docker para Odoo</a></li>
<li><a href="#arquitectura">Arquitectura Docker para Odoo</a></li>
<li><a href="#docker-compose">Docker Compose Configuracion</a></li>
<li><a href="#optimizacion">Optimizacion de Rendimiento</a></li>
<li><a href="#proveedores">Mejores Proveedores Docker</a></li>
<li><a href="#backups">Backup y Recuperacion</a></li>
<li><a href="#seguridad">Seguridad en Contenedores</a></li>
<li><a href="#orquestacion">Swarm vs Kubernetes</a></li>
<li><a href="#faq">الأسئلة الشائعة (8+)</a></li>
<li><a href="#relacionados">مقالات ذات صلة</a></li>
</ol>
</nav>
<!-- Introduction Section -->
<section id="introduccion" class="content-section fade-in">
<h2>Por que Docker es la Mejor Opcion para Odoo en 2026</h2>
<p>La contenedorizacion ha revolucionado la forma en que desplegamos y gestionamos aplicaciones empresariales. En 2026, Docker se ha consolidado como el estandar de facto para despliegues de Odoo, ofreciendo ventajas significativas sobre las instalaciones tradicionales en servidores bare metal.</p>
<p>Segun las ultimas estadisticas de la industria, mas del 85% de las empresas que implementan Odoo en produccion utilizan algun tipo de contenedorizacion. Docker, con su ecosistema maduro y amplia documentacion, lidera este mercado con una cuota superior al 70%.</p>
<div class="highlight-box">
<h4>Conclusion Clave</h4>
<p>Docker no solo simplifica el despliegue de Odoo, sino que reduce el tiempo de configuracion de dias a minutos, garantiza consistencia entre entornos de desarrollo y produccion, y facilita la escalabilidad horizontal cuando tu negocio crece.</p>
</div>
<h3>Beneficios de Odoo Containerizado</h3>
<div class="pros-cons">
<div class="pros">
<h4>Ventajas de Docker</h4>
<ul>
<li>Aislamiento completo de dependencias</li>
<li>Reproducibilidad garantizada</li>
<li>Escalabilidad horizontal sencilla</li>
<li>Rollback instantaneo ante fallos</li>
<li>Menor consumo de recursos vs VMs</li>
<li>CI/CD integrado nativamente</li>
<li>Portabilidad entre proveedores</li>
</ul>
</div>
<div class="cons">
<h4>Consideraciones</h4>
<ul>
<li>Curva de aprendizaje inicial</li>
<li>Overhead de 2-5% en rendimiento</li>
<li>Complejidad en networking</li>
<li>Gestion de volumenes persistentes</li>
<li>Requiere monitorizacion adicional</li>
</ul>
</div>
</div>
<h3>Docker vs Instalacion Tradicional</h3>
<p>La instalacion tradicional de Odoo implica configurar manualmente Python, PostgreSQL, dependencias del sistema, wkhtmltopdf y multiples bibliotecas. Este proceso puede tardar horas y es propenso a errores. Con Docker, todo esto se reduce a un simple comando que levanta un entorno identico cada vez.</p>
<p>Ademas, cuando necesitas actualizar Odoo o migrar a un nuevo servidor, Docker permite hacer pull de la nueva imagen, probarla en paralelo y cambiar el trafico sin interrupcion. En una instalacion tradicional, este proceso requiere planificacion cuidadosa y frecuentemente implica downtime.</p>
</section>
<!-- Architecture Diagram Section -->
<section class="chart-section fade-in">
<h3 id="arquitectura">Arquitectura Docker para Odoo: Diagrama Interactivo</h3>
<p>Explora la arquitectura recomendada para desplegar Odoo con Docker. Pasa el cursor sobre cada componente para ver detalles de configuracion.</p>
<div class="chart-container" id="architecture-diagram"></div>
</section>
<!-- Docker Compose Section -->
<section id="docker-compose" class="content-section fade-in">
<h2>Configuracion Docker Compose para Odoo 18</h2>
<p>Docker Compose es la herramienta preferida para definir y ejecutar aplicaciones multi-contenedor. Para Odoo, necesitamos al menos dos servicios: el servidor Odoo y la base de datos PostgreSQL. A continuacion presentamos configuraciones desde basica hasta produccion.</p>
<h3>Configuracion أساسي para Desarrollo</h3>
<p>Esta configuracion es ideal para desarrollo local y pruebas. Incluye los servicios esenciales con configuracion minima.</p>
<div class="code-block" data-lang="yaml">
<pre><span class="comment"># docker-compose.yml - Configuracion basica</span>
<span class="keyword">version</span>: <span class="string">'3.8'</span>
<span class="keyword">services</span>:
<span class="property">odoo</span>:
<span class="keyword">image</span>: <span class="string">odoo:18</span>
<span class="keyword">depends_on</span>:
- <span class="string">db</span>
<span class="keyword">ports</span>:
- <span class="string">"8069:8069"</span>
<span class="keyword">volumes</span>:
- <span class="string">odoo-data:/var/lib/odoo</span>
- <span class="string">./config:/etc/odoo</span>
- <span class="string">./addons:/mnt/extra-addons</span>
<span class="keyword">environment</span>:
- <span class="property">HOST</span>=<span class="string">db</span>
- <span class="property">USER</span>=<span class="string">odoo</span>
- <span class="property">PASSWORD</span>=<span class="string">odoo_password</span>
<span class="property">db</span>:
<span class="keyword">image</span>: <span class="string">postgres:16</span>
<span class="keyword">environment</span>:
- <span class="property">POSTGRES_DB</span>=<span class="string">postgres</span>
- <span class="property">POSTGRES_USER</span>=<span class="string">odoo</span>
- <span class="property">POSTGRES_PASSWORD</span>=<span class="string">odoo_password</span>
<span class="keyword">volumes</span>:
- <span class="string">db-data:/var/lib/postgresql/data</span>
<span class="keyword">volumes</span>:
<span class="property">odoo-data</span>:
<span class="property">db-data</span>:</pre>
</div>
<h3>Configuracion متقدم para Produccion</h3>
<p>Para entornos de produccion, necesitamos anadir reverse proxy con SSL, limites de recursos, healthchecks, logging y configuracion de red aislada.</p>
<div class="code-block" data-lang="yaml">
<pre><span class="comment"># docker-compose.prod.yml - Configuracion produccion</span>
<span class="keyword">version</span>: <span class="string">'3.8'</span>
<span class="keyword">services</span>:
<span class="property">traefik</span>:
<span class="keyword">image</span>: <span class="string">traefik:v3.0</span>
<span class="keyword">command</span>:
- <span class="string">"--api.dashboard=true"</span>
- <span class="string">"--providers.docker=true"</span>
- <span class="string">"--entrypoints.web.address=:80"</span>
- <span class="string">"--entrypoints.websecure.address=:443"</span>
- <span class="string">"--certificatesresolvers.letsencrypt.acme.email=admin@tudominio.com"</span>
- <span class="string">"--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"</span>
- <span class="string">"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"</span>
<span class="keyword">ports</span>:
- <span class="string">"80:80"</span>
- <span class="string">"443:443"</span>
<span class="keyword">volumes</span>:
- <span class="string">/var/run/docker.sock:/var/run/docker.sock:ro</span>
- <span class="string">letsencrypt:/letsencrypt</span>
<span class="keyword">networks</span>:
- <span class="string">odoo-network</span>
<span class="property">odoo</span>:
<span class="keyword">image</span>: <span class="string">odoo:18</span>
<span class="keyword">depends_on</span>:
<span class="property">db</span>:
<span class="keyword">condition</span>: <span class="string">service_healthy</span>
<span class="keyword">labels</span>:
- <span class="string">"traefik.enable=true"</span>
- <span class="string">"traefik.http.routers.odoo.rule=Host(`erp.tudominio.com`)"</span>
- <span class="string">"traefik.http.routers.odoo.tls.certresolver=letsencrypt"</span>
- <span class="string">"traefik.http.services.odoo.loadbalancer.server.port=8069"</span>
<span class="keyword">volumes</span>:
- <span class="string">odoo-data:/var/lib/odoo</span>
- <span class="string">./config/odoo.conf:/etc/odoo/odoo.conf:ro</span>
- <span class="string">./addons:/mnt/extra-addons:ro</span>
<span class="keyword">environment</span>:
- <span class="property">HOST</span>=<span class="string">db</span>
- <span class="property">USER</span>=<span class="string">odoo</span>
- <span class="property">PASSWORD_FILE</span>=<span class="string">/run/secrets/db_password</span>
<span class="keyword">secrets</span>:
- <span class="string">db_password</span>
<span class="keyword">deploy</span>:
<span class="keyword">resources</span>:
<span class="keyword">limits</span>:
<span class="property">cpus</span>: <span class="string">'2'</span>
<span class="property">memory</span>: <span class="string">4G</span>
<span class="keyword">reservations</span>:
<span class="property">cpus</span>: <span class="string">'1'</span>
<span class="property">memory</span>: <span class="string">2G</span>
<span class="keyword">healthcheck</span>:
<span class="keyword">test</span>: [<span class="string">"CMD"</span>, <span class="string">"curl"</span>, <span class="string">"-f"</span>, <span class="string">"http://localhost:8069/web/health"</span>]
<span class="keyword">interval</span>: <span class="string">30s</span>
<span class="keyword">timeout</span>: <span class="string">10s</span>
<span class="keyword">retries</span>: <span class="number">3</span>
<span class="keyword">networks</span>:
- <span class="string">odoo-network</span>
<span class="keyword">restart</span>: <span class="string">unless-stopped</span>
<span class="property">db</span>:
<span class="keyword">image</span>: <span class="string">postgres:16-alpine</span>
<span class="keyword">environment</span>:
- <span class="property">POSTGRES_DB</span>=<span class="string">postgres</span>
- <span class="property">POSTGRES_USER</span>=<span class="string">odoo</span>
- <span class="property">POSTGRES_PASSWORD_FILE</span>=<span class="string">/run/secrets/db_password</span>
<span class="keyword">secrets</span>:
- <span class="string">db_password</span>
<span class="keyword">volumes</span>:
- <span class="string">db-data:/var/lib/postgresql/data</span>
- <span class="string">./postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro</span>
<span class="keyword">command</span>: <span class="string">postgres -c config_file=/etc/postgresql/postgresql.conf</span>
<span class="keyword">deploy</span>:
<span class="keyword">resources</span>:
<span class="keyword">limits</span>:
<span class="property">cpus</span>: <span class="string">'2'</span>
<span class="property">memory</span>: <span class="string">2G</span>
<span class="keyword">healthcheck</span>:
<span class="keyword">test</span>: [<span class="string">"CMD-SHELL"</span>, <span class="string">"pg_isready -U odoo"</span>]
<span class="keyword">interval</span>: <span class="string">10s</span>
<span class="keyword">timeout</span>: <span class="string">5s</span>
<span class="keyword">retries</span>: <span class="number">5</span>
<span class="keyword">networks</span>:
- <span class="string">odoo-network</span>
<span class="keyword">restart</span>: <span class="string">unless-stopped</span>
<span class="keyword">secrets</span>:
<span class="property">db_password</span>:
<span class="keyword">file</span>: <span class="string">./secrets/db_password.txt</span>
<span class="keyword">volumes</span>:
<span class="property">odoo-data</span>:
<span class="property">db-data</span>:
<span class="property">letsencrypt</span>:
<span class="keyword">networks</span>:
<span class="property">odoo-network</span>:
<span class="keyword">driver</span>: <span class="string">bridge</span></pre>
</div>
<h3>Archivo de Configuracion odoo.conf</h3>
<div class="code-block" data-lang="ini">
<pre><span class="comment">; /config/odoo.conf - Configuracion optimizada</span>
[<span class="keyword">options</span>]
<span class="property">addons_path</span> = <span class="string">/mnt/extra-addons,/usr/lib/python3/dist-packages/odoo/addons</span>
<span class="property">data_dir</span> = <span class="string">/var/lib/odoo</span>
<span class="property">admin_passwd</span> = <span class="string">$pbkdf2-sha512$your_hashed_password</span>
<span class="comment">; Configuracion de workers para produccion</span>
<span class="property">workers</span> = <span class="number">4</span>
<span class="property">max_cron_threads</span> = <span class="number">2</span>
<span class="property">limit_memory_hard</span> = <span class="number">2684354560</span>
<span class="property">limit_memory_soft</span> = <span class="number">2147483648</span>
<span class="property">limit_request</span> = <span class="number">8192</span>
<span class="property">limit_time_cpu</span> = <span class="number">600</span>
<span class="property">limit_time_real</span> = <span class="number">1200</span>
<span class="property">limit_time_real_cron</span> = <span class="number">-1</span>
<span class="comment">; Proxy y seguridad</span>
<span class="property">proxy_mode</span> = <span class="string">True</span>
<span class="property">xmlrpc_interface</span> = <span class="string">0.0.0.0</span>
<span class="property">netrpc_interface</span> = <span class="string">0.0.0.0</span>
<span class="comment">; Logging</span>
<span class="property">logfile</span> = <span class="string">/var/log/odoo/odoo.log</span>
<span class="property">log_level</span> = <span class="string">warn</span>
<span class="property">log_handler</span> = <span class="string">:WARNING,werkzeug:WARNING,odoo.addons:WARNING</span>
<span class="comment">; Base de datos</span>
<span class="property">db_host</span> = <span class="string">db</span>
<span class="property">db_port</span> = <span class="number">5432</span>
<span class="property">db_maxconn</span> = <span class="number">64</span>
<span class="property">db_template</span> = <span class="string">template0</span></pre>
</div>
<div class="warning-box">
<h4>Importante: Seguridad de Passwords</h4>
<p>Nunca guardes passwords en texto plano en archivos de configuracion. Usa Docker secrets como se muestra en la configuracion de produccion, o variables de entorno inyectadas desde un sistema de gestion de secretos como HashiCorp Vault.</p>
</div>
</section>
<!-- Performance Comparison Chart -->
<section class="chart-section fade-in">
<h3 id="optimizacion">Comparativa de Rendimiento: Docker vs Nativo vs VM</h3>
<p>Este grafico muestra el rendimiento relativo de Odoo en diferentes entornos de despliegue. Los valores representan transacciones por segundo normalizadas.</p>
<div class="chart-container" id="performance-chart"></div>
</section>
<!-- Optimization Section -->
<section class="content-section fade-in">
<h2>Optimizacion de Rendimiento para Odoo Docker</h2>
<p>Aunque Docker introduce un overhead minimo (2-5%), una configuracion incorrecta puede degradar significativamente el rendimiento. Estas son las optimizaciones esenciales para maximizar el rendimiento de tu Odoo containerizado.</p>
<h3>1. Configuracion de Workers</h3>
<p>El numero de workers determina cuantas peticiones puede manejar Odoo simultaneamente. La formula recomendada es:</p>
<div class="highlight-box">
<h4>Formula de Workers</h4>
<p><strong>workers = (CPU cores * 2) + 1</strong></p>
<p>Para un servidor con 4 cores: workers = (4 * 2) + 1 = 9 workers</p>
<p>Cada worker consume aproximadamente 150-300MB de RAM.</p>
</div>
<h3>2. Optimizacion de PostgreSQL</h3>
<div class="code-block" data-lang="conf">
<pre><span class="comment"># postgresql.conf optimizado para Odoo</span>
<span class="property">shared_buffers</span> = <span class="string">512MB</span>
<span class="property">effective_cache_size</span> = <span class="string">1536MB</span>
<span class="property">maintenance_work_mem</span> = <span class="string">128MB</span>
<span class="property">checkpoint_completion_target</span> = <span class="number">0.9</span>
<span class="property">wal_buffers</span> = <span class="string">16MB</span>
<span class="property">default_statistics_target</span> = <span class="number">100</span>
<span class="property">random_page_cost</span> = <span class="number">1.1</span>
<span class="property">effective_io_concurrency</span> = <span class="number">200</span>
<span class="property">work_mem</span> = <span class="string">10MB</span>
<span class="property">min_wal_size</span> = <span class="string">1GB</span>
<span class="property">max_wal_size</span> = <span class="string">4GB</span>
<span class="property">max_worker_processes</span> = <span class="number">4</span>
<span class="property">max_parallel_workers_per_gather</span> = <span class="number">2</span>
<span class="property">max_parallel_workers</span> = <span class="number">4</span>
<span class="property">max_parallel_maintenance_workers</span> = <span class="number">2</span></pre>
</div>
<h3>3. Volumenes: Bind Mounts vs Named Volumes</h3>
<p>Para datos de produccion, usa named volumes gestionados por Docker. Son mas eficientes y faciles de respaldar. Los bind mounts son utiles solo para desarrollo cuando necesitas editar archivos directamente.</p>
<h3>4. Network Mode</h3>
<p>Para comunicacion entre contenedores en el mismo host, usa networks bridge personalizadas. Evita el modo host excepto para debug. Las redes overlay son necesarias para Docker Swarm multi-nodo.</p>
<ul class="step-list">
<li><strong>Usa imagenes Alpine</strong> - postgres:16-alpine es 70% mas pequena que la imagen estandar</li>
<li><strong>Habilita BuildKit</strong> - DOCKER_BUILDKIT=1 acelera builds con cache de capas</li>
<li><strong>Limita logs</strong> - Configura max-size y max-file en logging driver</li>
<li><strong>Monitoriza con cAdvisor</strong> - Metricas de contenedores en tiempo real</li>
<li><strong>Usa SSD para volumenes</strong> - Minimo NVMe para base de datos</li>
</ul>
</section>
<!-- Providers Section -->
<section id="proveedores" class="content-section fade-in">
<h2>Mejores Proveedores de Docker Hosting para Odoo</h2>
<p>Elegir el proveedor correcto para tu hosting Docker es crucial. Evaluamos los principales proveedores considerando precio, rendimiento, facilidad de uso y soporte para contenedores.</p>
<div class="provider-grid">
<!-- DigitalOcean -->
<div class="provider-card featured">
<span class="badge">Mejor Calidad-Precio</span>
<h4>DigitalOcean</h4>
<p class="price">25EUR<span class="price-period">/mes</span></p>
<ul>
<li>Droplets optimizados para Docker</li>
<li>App Platform con contenedores</li>
<li>Managed Kubernetes disponible</li>
<li>Backups automaticos incluidos</li>
<li>Excelente documentacion</li>
<li>Datacenter en Frankfurt</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
<!-- Hetzner -->
<div class="provider-card featured">
<span class="badge">Mejor Rendimiento/Precio</span>
<h4>Hetzner Cloud</h4>
<p class="price">15EUR<span class="price-period">/mes</span></p>
<ul>
<li>CPUs AMD EPYC dedicadas</li>
<li>NVMe SSD incluido</li>
<li>Docker preinstalado opcional</li>
<li>Firewall cloud gratuito</li>
<li>Datacenters en Alemania/Finlandia</li>
<li>IPv6 nativo</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
<!-- AWS -->
<div class="provider-card">
<span class="badge">Enterprise</span>
<h4>AWS ECS/Fargate</h4>
<p class="price">50EUR<span class="price-period">/mes min</span></p>
<ul>
<li>Orquestacion empresarial</li>
<li>Auto-scaling automatico</li>
<li>Integracion con servicios AWS</li>
<li>Fargate serverless</li>
<li>Multi-region</li>
<li>Cumplimiento ISO/SOC</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
<!-- Google Cloud -->
<div class="provider-card">
<h4>Google Cloud Run</h4>
<p class="price">35EUR<span class="price-period">/mes</span></p>
<ul>
<li>Contenedores serverless</li>
<li>Escala a cero automatico</li>
<li>HTTPS automatico</li>
<li>Integracion Cloud SQL</li>
<li>CI/CD con Cloud Build</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
<!-- Vultr -->
<div class="provider-card">
<h4>Vultr Kubernetes</h4>
<p class="price">20EUR<span class="price-period">/mes</span></p>
<ul>
<li>Kubernetes gestionado</li>
<li>High-frequency compute</li>
<li>Storage NVMe</li>
<li>32 datacenters globales</li>
<li>API completa</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
<!-- Contabo -->
<div class="provider-card">
<h4>Contabo VPS</h4>
<p class="price">12EUR<span class="price-period">/mes</span></p>
<ul>
<li>Mejor precio del mercado</li>
<li>Recursos generosos</li>
<li>Docker no gestionado</li>
<li>Ideal para desarrollo</li>
<li>Soporte basico</li>
</ul>
<a href="#" class="cta-btn">Ver Planes</a>
</div>
</div>
</section>
<!-- Provider Comparison Chart -->
<section class="chart-section fade-in">
<h3>Comparativa de Proveedores Docker: Radar de الميزةs</h3>
<p>Visualiza como se comparan los principales proveedores de Docker hosting en 6 dimensiones clave para despliegues de Odoo.</p>
<div class="chart-container" id="radar-chart"></div>
</section>
<!-- Backup Section -->
<section id="backups" class="content-section fade-in">
<h2>Estrategias de Backup y Recuperacion</h2>
<p>Un plan de backup robusto es critico para cualquier despliegue de Odoo en produccion. Con Docker, necesitamos respaldar tres componentes: la base de datos PostgreSQL, el filestore de Odoo y la configuracion.</p>
<h3>Script de Backup Automatizado</h3>
<div class="code-block" data-lang="bash">
<pre><span class="comment">#!/bin/bash</span>
<span class="comment"># backup-odoo.sh - Script de backup completo</span>
<span class="property">BACKUP_DIR</span>=<span class="string">"/backups/odoo/$(date +%Y-%m-%d)"</span>
<span class="property">RETENTION_DAYS</span>=<span class="number">30</span>
<span class="property">CONTAINER_DB</span>=<span class="string">"odoo_db_1"</span>
<span class="property">CONTAINER_ODOO</span>=<span class="string">"odoo_odoo_1"</span>
<span class="comment"># Crear directorio de backup</span>
<span class="keyword">mkdir</span> -p <span class="string">"$BACKUP_DIR"</span>
<span class="comment"># Backup de PostgreSQL</span>
<span class="keyword">echo</span> <span class="string">"[$(date)] Iniciando backup de PostgreSQL..."</span>
<span class="keyword">docker</span> exec <span class="string">$CONTAINER_DB</span> pg_dumpall -U odoo | \
gzip > <span class="string">"$BACKUP_DIR/postgres_full_$(date +%H%M).sql.gz"</span>
<span class="comment"># Backup del filestore</span>
<span class="keyword">echo</span> <span class="string">"[$(date)] Iniciando backup del filestore..."</span>
<span class="keyword">docker</span> run --rm \
-v odoo_odoo-data:/source:ro \
-v <span class="string">$BACKUP_DIR</span>:/backup \
alpine tar -czf /backup/filestore_$(date +%H%M).tar.gz -C /source .
<span class="comment"># Backup de configuracion</span>
<span class="keyword">echo</span> <span class="string">"[$(date)] Backup de configuracion..."</span>
<span class="keyword">cp</span> -r ./config <span class="string">"$BACKUP_DIR/config"</span>
<span class="keyword">cp</span> docker-compose.yml <span class="string">"$BACKUP_DIR/"</span>
<span class="comment"># Limpiar backups antiguos</span>
<span class="keyword">echo</span> <span class="string">"[$(date)] Limpiando backups mayores a $RETENTION_DAYS dias..."</span>
<span class="keyword">find</span> /backups/odoo -type d -mtime +<span class="string">$RETENTION_DAYS</span> -exec rm -rf {} +
<span class="comment"># نعمncronizar a almacenamiento remoto (opcional)</span>
<span class="comment"># rclone sync $BACKUP_DIR remote:odoo-backups/$(date +%Y-%m-%d)</span>
<span class="keyword">echo</span> <span class="string">"[$(date)] Backup completado en $BACKUP_DIR"</span></pre>
</div>
<h3>Programar Backups con Cron</h3>
<div class="code-block" data-lang="cron">
<pre><span class="comment"># Editar crontab: crontab -e</span>
<span class="comment"># Backup diario a las 3:00 AM</span>
<span class="number">0</span> <span class="number">3</span> * * * /opt/scripts/backup-odoo.sh >> /var/log/odoo-backup.log <span class="number">2</span>>&<span class="number">1</span>
<span class="comment"># Backup incremental cada 6 horas</span>
<span class="number">0</span> */6 * * * /opt/scripts/backup-incremental.sh >> /var/log/odoo-backup.log <span class="number">2</span>>&<span class="number">1</span></pre>
</div>
<h3>Restauracion de Backup</h3>
<div class="code-block" data-lang="bash">
<pre><span class="comment">#!/bin/bash</span>
<span class="comment"># restore-odoo.sh - Script de restauracion</span>
<span class="property">BACKUP_DATE</span>=<span class="string">"$1"</span> <span class="comment"># Formato: 2026-01-25</span>
<span class="property">BACKUP_DIR</span>=<span class="string">"/backups/odoo/$BACKUP_DATE"</span>
<span class="comment"># Detener Odoo</span>
<span class="keyword">docker</span> compose stop odoo
<span class="comment"># Restaurar PostgreSQL</span>
<span class="keyword">gunzip</span> -c <span class="string">"$BACKUP_DIR/postgres_full_*.sql.gz"</span> | \
<span class="keyword">docker</span> exec -i odoo_db_1 psql -U odoo
<span class="comment"># Restaurar filestore</span>
<span class="keyword">docker</span> run --rm \
-v odoo_odoo-data:/target \
-v <span class="string">$BACKUP_DIR</span>:/backup:ro \
alpine sh -c <span class="string">"rm -rf /target/* && tar -xzf /backup/filestore_*.tar.gz -C /target"</span>
<span class="comment"># Reiniciar servicios</span>
<span class="keyword">docker</span> compose up -d
<span class="keyword">echo</span> <span class="string">"Restauracion completada desde $BACKUP_DATE"</span></pre>
</div>
<div class="highlight-box">
<h4>Regla 3-2-1 de Backups</h4>
<p><strong>3 copias</strong> de tus datos, en <strong>2 tipos</strong> de almacenamiento diferentes, con <strong>1 copia offsite</strong>. Ejemplo: disco local + NAS + cloud (S3/Backblaze).</p>
</div>
</section>
<!-- Security Section -->
<section id="seguridad" class="content-section fade-in">
<h2>Seguridad en Contenedores Odoo</h2>
<p>La seguridad en entornos Docker requiere atencion en multiples capas: imagen base, configuracion del contenedor, red y host. Estas son las mejores practicas para asegurar tu Odoo containerizado.</p>
<h3>Checklist de Seguridad Docker</h3>
<ul class="step-list">
<li><strong>Usa imagenes oficiales</strong> - Solo odoo:18 o builds propios basados en oficial</li>
<li><strong>Escanea vulnerabilidades</strong> - Trivy o Snyk en CI/CD pipeline</li>
<li><strong>Usuario no-root</strong> - Configura USER en Dockerfile</li>
<li><strong>Read-only filesystem</strong> - Monta volumenes como :ro cuando sea posible</li>
<li><strong>Secrets management</strong> - Docker secrets o Vault, nunca env vars en compose</li>
<li><strong>Network isolation</strong> - Redes bridge personalizadas, nunca host mode</li>
<li><strong>Resource limits</strong> - Limita CPU y memoria para prevenir DoS</li>
<li><strong>Actualizaciones regulares</strong> - Pull semanal de imagenes base</li>
</ul>
<h3>Configuracion de Firewall (UFW)</h3>
<div class="code-block" data-lang="bash">
<pre><span class="comment"># Configuracion basica de firewall para Docker host</span>
<span class="keyword">ufw</span> default deny incoming
<span class="keyword">ufw</span> default allow outgoing
<span class="keyword">ufw</span> allow ssh
<span class="keyword">ufw</span> allow <span class="number">80</span>/tcp
<span class="keyword">ufw</span> allow <span class="number">443</span>/tcp
<span class="keyword">ufw</span> enable
<span class="comment"># Importante: Docker modifica iptables directamente</span>
<span class="comment"># Configura DOCKER_OPTS para respetar UFW</span>
<span class="keyword">echo</span> <span class="string">'{"iptables": false}'</span> | sudo tee /etc/docker/daemon.json</pre>
</div>
</section>
<!-- Orchestration Comparison -->
<section class="chart-section fade-in">
<h3 id="orquestacion">Docker Swarm vs Kubernetes: Arbol de Decision</h3>
<p>Navega por este arbol interactivo para determinar que orquestador es mejor para tu caso de uso con Odoo.</p>
<div class="chart-container" id="decision-tree"></div>
</section>
<section class="content-section fade-in">
<h2>Orquestacion: Swarm vs Kubernetes</h2>
<p>Cuando tu despliegue de Odoo crece, necesitas orquestacion para alta disponibilidad, balanceo de carga y actualizaciones sin downtime. Las dos opciones principales son Docker Swarm y Kubernetes.</p>
<h3>Docker Swarm</h3>
<p>Docker Swarm es la solucion de orquestacion nativa de Docker. Es mas simple de configurar y gestionar, ideal para equipos pequenos o despliegues de tamano medio.</p>
<div class="code-block" data-lang="bash">
<pre><span class="comment"># Inicializar Swarm</span>
<span class="keyword">docker</span> swarm init --advertise-addr <span class="number">192.168.1.10</span>
<span class="comment"># Desplegar stack de Odoo</span>
<span class="keyword">docker</span> stack deploy -c docker-compose.prod.yml odoo
<span class="comment"># Escalar servicio</span>
<span class="keyword">docker</span> service scale odoo_odoo=<span class="number">3</span>
<span class="comment"># Rolling update</span>
<span class="keyword">docker</span> service update --image odoo:<span class="number">18.1</span> odoo_odoo</pre>
</div>
<h3>Kubernetes (K8s)</h3>
<p>Kubernetes es el estandar de la industria para orquestacion de contenedores a gran escala. Ofrece mas funcionalidades pero con mayor complejidad operativa.</p>
<div class="comparison-table">
<table>
<thead>
<tr>
<th>الميزة</th>
<th>Docker Swarm</th>
<th>Kubernetes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Complejidad</td>
<td class="check">Baja</td>
<td class="cross">Alta</td>
</tr>
<tr>
<td>Curva aprendizaje</td>
<td class="check">Dias</td>
<td class="cross">Semanas/Meses</td>
</tr>
<tr>
<td>Escalabilidad</td>
<td>Hasta 1000 nodos</td>
<td class="check">5000+ nodos</td>
</tr>
<tr>
<td>Auto-healing</td>
<td class="check">نعم</td>
<td class="check">نعم (avanzado)</td>
</tr>
<tr>
<td>Rolling updates</td>
<td class="check">نعم</td>
<td class="check">نعم (con mas control)</td>
</tr>
<tr>
<td>Service mesh</td>
<td class="cross">محدود</td>
<td class="check">Istio/Linkerd</td>
</tr>
<tr>
<td>Ecosistema</td>
<td>Docker nativo</td>
<td class="check">Muy amplio</td>
</tr>
<tr>
<td>موصى به para Odoo</td>
<td class="check">< 100 usuarios</td>
<td class="check">> 100 usuarios</td>
</tr>
</tbody>
</table>
</div>
<div class="highlight-box">
<h4>Nuestra Recomendacion</h4>
<p>Para el 90% de despliegues de Odoo, <strong>Docker Swarm</strong> es suficiente y mucho mas facil de mantener. Solo considera Kubernetes si tienes mas de 100 usuarios concurrentes, necesitas multi-region activo-activo, o ya tienes infraestructura K8s existente.</p>
</div>
</section>
<!-- CTA Banner -->
<section class="cta-banner fade-in">
<h3>Despliega Odoo con Docker Facilmente</h3>
<p>Descarga nuestra plantilla Docker Compose optimizada para produccion. Incluye Traefik, SSL automatico, backups y monitorizacion.</p>
<a href="#" class="cta-btn">Descargar Plantilla Gratis</a>
</section>
<!-- FAQ Section -->
<section id="faq" class="faq-section fade-in">
<h2>Preguntas Frecuentes sobre Odoo Docker</h2>
<div class="faq-item">
<div class="faq-question">Es Docker recomendable para Odoo en produccion?</div>
<div class="faq-answer">نعم, Docker es altamente recomendable para Odoo en produccion en 2026. Ofrece aislamiento, reproducibilidad, escalabilidad horizontal y facilita los despliegues. Empresas como Amazon, Google y Microsoft utilizan contenedores en produccion. La clave esta en una correcta configuracion de volumenes persistentes, redes y orquestacion.</div>
</div>
<div class="faq-item">
<div class="faq-question">Cuantos recursos necesita Odoo en Docker?</div>
<div class="faq-answer">Para Odoo 18 en Docker se recomienda: 4GB RAM minimo (8GB recomendado), 2 CPU cores (4 recomendado), 40GB SSD para volumenes. Docker anade aproximadamente un 2-5% de overhead comparado con instalacion nativa, pero los beneficios de gestion superan ampliamente este coste.</div>
</div>
<div class="faq-item">
<div class="faq-question">Como hago backup de Odoo en Docker?</div>
<div class="faq-answer">Para backup de Odoo en Docker: 1) Backup de PostgreSQL con pg_dump desde el contenedor, 2) Backup del volumen filestore, 3) Backup de configuracion odoo.conf. Automatiza con cron jobs y docker exec. Recomendamos backups incrementales diarios y completos semanales con retencion de 30 dias.</div>
</div>
<div class="faq-item">
<div class="faq-question">Puedo usar Docker Compose para multiples instancias de Odoo?</div>
<div class="faq-answer">نعم, Docker Compose es ideal para multi-tenant Odoo. Puedes definir multiples servicios Odoo en el mismo archivo compose, cada uno con su propia base de datos. Usa Traefik o Nginx como reverse proxy para enrutar dominios a cada instancia. Esto es perfecto para agencias que gestionan varios clientes.</div>
</div>
<div class="faq-item">
<div class="faq-question">Docker Swarm o Kubernetes para Odoo?</div>
<div class="faq-answer">Para la mayoria de despliegues Odoo, Docker Swarm es suficiente y mas simple de gestionar. Kubernetes es recomendable solo para despliegues muy grandes (100+ usuarios concurrentes) o si ya tienes infraestructura K8s. Docker Swarm ofrece alta disponibilidad, balanceo de carga y escalado con menor complejidad operativa.</div>
</div>
<div class="faq-item">
<div class="faq-question">Como actualizo Odoo en Docker sin downtime?</div>
<div class="faq-answer">Para actualizar Odoo en Docker con minimo downtime: 1) Pull de la nueva imagen, 2) Backup completo, 3) Levantar nuevo contenedor en paralelo, 4) Ejecutar migracion de base de datos, 5) Cambiar el proxy al nuevo contenedor, 6) Eliminar contenedor antiguo. Con Docker Swarm puedes hacer rolling updates automaticos.</div>
</div>
<div class="faq-item">
<div class="faq-question">Cual es la mejor imagen Docker para Odoo?</div>
<div class="faq-answer">La imagen oficial de Odoo (odoo:18) es la recomendada para produccion. Esta optimizada, actualizada regularmente y soportada. Para personalizaciones, crea tu propia imagen basada en la oficial con un Dockerfile. Evita imagenes de terceros no verificadas por razones de seguridad.</div>
</div>
<div class="faq-item">
<div class="faq-question">Como configuro SSL/HTTPS para Odoo en Docker?</div>
<div class="faq-answer">Para SSL en Odoo Docker usa un reverse proxy: 1) Traefik con Let's Encrypt automatico (recomendado), 2) Nginx con Certbot, 3) Caddy con SSL automatico. El proxy maneja SSL y redirige al contenedor Odoo via HTTP interno. Nunca expongas Odoo directamente a internet sin SSL.</div>
</div>
</section>
<!-- Related Articles -->
<section id="relacionados" class="content-section fade-in">
<h2>مقالات ذات صلة</h2>
<div class="related-articles">
<a href="/ar/odoo-hosting/mejor-hosting-odoo-2026/" class="related-card">
<div class="related-card-image">*</div>
<div class="related-card-content">
<h4>Mejor استضافة Odoo 2026</h4>
<p>Comparativa completa de proveedores</p>
</div>
</a>
<a href="/ar/odoo-hosting/vps-hosting-odoo-servidor-virtual-2025/" class="related-card">
<div class="related-card-image">[VPS]</div>
<div class="related-card-content">
<h4>VPS para Odoo: Guia Completa</h4>
<p>Configuracion de servidor virtual paso a paso</p>
</div>
</a>
<a href="/ar/odoo-hosting/requisitos-hosting-odoo-servidor-2025/" class="related-card">
<div class="related-card-image">[REQ]</div>
<div class="related-card-content">
<h4>Requisitos Tecnicos Odoo</h4>
<p>Especificaciones de hardware y software</p>
</div>
</a>
<a href="/ar/odoo-hosting/odoo-sh-hosting-oficial-plataforma-2025/" class="related-card">
<div class="related-card-image">[SH]</div>
<div class="related-card-content">
<h4>Odoo.sh: Plataforma Oficial</h4>
<p>Analisis completo de Odoo.sh</p>
</div>
</a>
<a href="/ar/odoo-hosting/hosting-odoo-gratis-gratuito-2025/" class="related-card">
<div class="related-card-image">[FREE]</div>
<div class="related-card-content">
<h4>استضافة Odoo Gratis</h4>
<p>Opciones gratuitas para comenzar</p>
</div>
</a>
<a href="/ar/odoo-hosting/precios-hosting-odoo-costos-2025/" class="related-card">
<div class="related-card-image">[EUR]</div>
<div class="related-card-content">
<h4>Precios استضافة Odoo</h4>
<p>Comparativa detallada de costos</p>
</div>
</a>
</div>
</section>
</main>
<!-- EEAT Author Section -->
<div id="eeat-section"></div>
<div id="footer"><footer class="site-footer"><div class="container"><div class="footer-cols"><div><strong>Odoo Expertos</strong></div><nav class="footer-nav"><a href="/ar/">Inicio</a><a href="/ar/odoo/">Odoo</a><a href="/ar/odoo-ia/">Odoo AI</a><a href="/ar/odoo-hosting/">Odoo Hosting</a><a href="/ar/mapa-del-sitio/">Sitemap</a></nav></div><div class="footer-bottom"><small>&copy; 2026 Odoo Expertos</small></div></div></footer></div><nav class="footer-nav"><a href="/ar/">Inicio</a><a href="/ar/odoo/">Odoo</a><a href="/ar/odoo-ia/">Odoo AI</a><a href="/ar/odoo-hosting/">Odoo Hosting</a><a href="/ar/mapa-del-sitio/">Sitemap</a></nav></div><div class="footer-bottom"><small>&copy; 2026 Odoo Expertos</small></div></div></footer></div>
<!-- Scripts -->
<script src="/js/language-router.js"></script>
<script src="/components/header.min.js"></script>
<script src="/components/footer.min.js"></script>
<script src="/components/eeat.min.js"></script>
<script src="/js/cookie-banner.min.js"></script>
<!-- D3.js -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// Initialize components
// Scroll animation observer
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));
// FAQ Toggle
document.querySelectorAll('.faq-question').forEach(question => {
question.addEventListener('click', () => {
const item = question.parentElement;
item.classList.toggle('open');
});
});
// Tooltip
const tooltip = d3.select('body').append('div')
.attr('class', 'd3-tooltip')
.style('opacity', 0);
// Color scales
const blueScale = d3.scaleOrdinal()
.range(['#2196F3', '#64B5F6', '#90CAF9', '#42A5F5', '#1E88E5', '#1565C0']);
const colorScale = d3.scaleOrdinal()
.range(['#2196F3', '#4CAF50', '#FF9800', '#9C27B0', '#00BCD4', '#E91E63']);
// Architecture Diagram
function renderArchitectureDiagram() {
const container = d3.select('#architecture-diagram');
if (container.empty()) return;
const width = container.node().getBoundingClientRect().width;
const height = 450;
container.html('');
const svg = container.append('svg')
.attr('width', width)
.attr('height', height);
// Architecture components
const components = [
{ id: 'internet', label: 'Internet', x: width/2, y: 40, type: 'external', desc: 'Trafico de usuarios y APIs externas' },
{ id: 'traefik', label: 'Traefik/Nginx', x: width/2, y: 120, type: 'proxy', desc: 'Reverse proxy con SSL/TLS, balanceo de carga' },
{ id: 'odoo1', label: 'Odoo Container 1', x: width/3, y: 220, type: 'app', desc: 'Instancia Odoo con workers configurados' },
{ id: 'odoo2', label: 'Odoo Container 2', x: 2*width/3, y: 220, type: 'app', desc: 'Replica para alta disponibilidad' },
{ id: 'postgres', label: 'PostgreSQL', x: width/3, y: 330, type: 'db', desc: 'Base de datos con volumenes persistentes' },
{ id: 'redis', label: 'Redis Cache', x: 2*width/3, y: 330, type: 'cache', desc: 'Cache de sesiones y cola de trabajos' },
{ id: 'volumes', label: 'Docker Volumes', x: width/2, y: 410, type: 'storage', desc: 'Filestore y datos persistentes' }
];
const connections = [
{ from: 'internet', to: 'traefik' },
{ from: 'traefik', to: 'odoo1' },
{ from: 'traefik', to: 'odoo2' },
{ from: 'odoo1', to: 'postgres' },
{ from: 'odoo2', to: 'postgres' },
{ from: 'odoo1', to: 'redis' },
{ from: 'odoo2', to: 'redis' },
{ from: 'postgres', to: 'volumes' },
{ from: 'odoo1', to: 'volumes' },
{ from: 'odoo2', to: 'volumes' }
];
// Draw connections
connections.forEach((conn, i) => {
const from = components.find(c => c.id === conn.from);
const to = components.find(c => c.id === conn.to);
svg.append('line')
.attr('x1', from.x)
.attr('y1', from.y + 20)
.attr('x2', to.x)
.attr('y2', to.y - 20)
.attr('stroke', '#444')
.attr('stroke-width', 2)
.attr('stroke-dasharray', '5,5')
.attr('opacity', 0)
.transition()
.duration(500)
.delay(i * 50)
.attr('opacity', 0.7);
});
// Color by type
const typeColors = {
external: '#9E9E9E',
proxy: '#2196F3',
app: '#4CAF50',
db: '#FF9800',
cache: '#E91E63',
storage: '#9C27B0'
};
// Draw components
components.forEach((comp, i) => {
const g = svg.append('g')
.attr('transform', `translate(${comp.x}, ${comp.y})`)
.style('cursor', 'pointer')
.style('opacity', 0);
// Rectangle
g.append('rect')
.attr('x', -70)
.attr('y', -20)
.attr('width', 140)
.attr('height', 40)
.attr('rx', 8)
.attr('fill', typeColors[comp.type])
.attr('fill-opacity', 0.2)
.attr('stroke', typeColors[comp.type])
.attr('stroke-width', 2);
// Label
g.append('text')
.attr('text-anchor', 'middle')
.attr('dy', 5)
.attr('fill', '#e0e0e0')
.attr('font-size', '12px')
.attr('font-weight', '500')
.text(comp.label);
// Icon based on type
const icons = {
external: 'WWW',
proxy: 'LB',
app: 'APP',
db: 'DB',
cache: '$',
storage: 'VOL'
};
g.append('text')
.attr('x', -55)
.attr('dy', 5)
.attr('fill', typeColors[comp.type])
.attr('font-size', '10px')
.attr('font-family', 'monospace')
.text(icons[comp.type]);
// Hover effects
g.on('mouseover', function(event) {
d3.select(this).select('rect')
.attr('fill-opacity', 0.4)
.attr('stroke-width', 3);
tooltip.transition().duration(200).style('opacity', 1);
tooltip.html(`<strong>${comp.label}</strong><br>${comp.desc}`)
.style('left', (event.pageX + 15) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
d3.select(this).select('rect')
.attr('fill-opacity', 0.2)
.attr('stroke-width', 2);
tooltip.transition().duration(200).style('opacity', 0);
});
g.transition()
.duration(500)
.delay(i * 100)
.style('opacity', 1);
});
// Legend
const legend = svg.append('g')
.attr('transform', `translate(20, 20)`);
Object.entries(typeColors).forEach(([type, color], i) => {
const labels = {
external: 'Externo',
proxy: 'Proxy',
app: 'Aplicacion',
db: 'Base Datos',
cache: 'Cache',
storage: 'Almacenamiento'
};
const g = legend.append('g')
.attr('transform', `translate(0, ${i * 22})`);
g.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('fill', color)
.attr('rx', 3);
g.append('text')
.attr('x', 22)
.attr('y', 12)
.attr('fill', '#888')
.attr('font-size', '11px')
.text(labels[type]);
});
}
// Performance Comparison Chart
function renderPerformanceChart() {
const container = d3.select('#performance-chart');
if (container.empty()) return;
const width = container.node().getBoundingClientRect().width;
const height = 400;
const margin = { top: 40, right: 30, bottom: 80, left: 60 };
container.html('');
const svg = container.append('svg')
.attr('width', width)
.attr('height', height);
const data = [
{ category: 'Bare Metal', docker: 0, native: 100, vm: 0 },
{ category: 'Docker', docker: 97, native: 0, vm: 0 },
{ category: 'Docker Swarm', docker: 95, native: 0, vm: 0 },
{ category: 'Kubernetes', docker: 93, native: 0, vm: 0 },
{ category: 'VM (KVM)', docker: 0, native: 0, vm: 85 },
{ category: 'VM (VMware)', docker: 0, native: 0, vm: 82 }
];
const categories = data.map(d => d.category);
const metrics = ['native', 'docker', 'vm'];
const metricLabels = { native: 'Nativo', docker: 'Docker', vm: 'Maquina Virtual' };
const metricColors = { native: '#4CAF50', docker: '#2196F3', vm: '#FF9800' };
const xScale = d3.scaleBand()
.domain(categories)
.range([margin.left, width - margin.right])
.padding(0.3);
const yScale = d3.scaleLinear()
.domain([0, 105])
.range([height - margin.bottom, margin.top]);
// Grid
svg.append('g')
.attr('class', 'grid')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale).tickنعمze(-(width - margin.left - margin.right)).tickFormat(''))
.selectAll('line').attr('stroke', '#333');
// X axis
svg.append('g')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale))
.selectAll('text')
.attr('fill', '#888')
.attr('transform', 'rotate(-25)')
.attr('text-anchor', 'end');
// Y axis
svg.append('g')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale).tickFormat(d => d + '%'))
.attr('color', '#888');
// Y label
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', -height / 2)
.attr('y', 15)
.attr('text-anchor', 'middle')
.attr('fill', '#888')
.attr('font-size', '12px')
.text('Rendimiento Relativo (%)');
// Bars
data.forEach((d, i) => {
metrics.forEach((metric) => {
if (d[metric] > 0) {
const bar = svg.append('rect')
.attr('x', xScale(d.category))
.attr('y', yScale(0))
.attr('width', xScale.bandwidth())
.attr('height', 0)
.attr('fill', metricColors[metric])
.attr('rx', 4)
.style('cursor', 'pointer');
bar.on('mouseover', function(event) {
d3.select(this).attr('fill-opacity', 0.8);
tooltip.transition().duration(200).style('opacity', 1);
tooltip.html(`<strong>${d.category}</strong><br>Rendimiento: ${d[metric]}%`)
.style('left', (event.pageX + 15) + 'px')
.style('top', (event.pageY - 28) + 'px');
})
.on('mouseout', function() {
d3.select(this).attr('fill-opacity', 1);
tooltip.transition().duration(200).style('opacity', 0);
});
bar.transition()
.duration(800)
.delay(i * 100)
.attr('y', yScale(d[metric]))
.attr('height', yScale(0) - yScale(d[metric]));
// Value label
svg.append('text')
.attr('x', xScale(d.category) + xScale.bandwidth() / 2)
.attr('y', yScale(d[metric]) - 8)
.attr('text-anchor', 'middle')
.attr('fill', '#e0e0e0')
.attr('font-size', '12px')
.attr('font-weight', '600')
.attr('opacity', 0)
.text(d[metric] + '%')
.transition()
.duration(500)
.delay(i * 100 + 400)
.attr('opacity', 1);
}
});
});
// Legend
const legend = svg.append('g')
.attr('transform', `translate(${width - 150}, 30)`);
Object.entries(metricLabels).forEach(([key, label], i) => {
const g = legend.append('g')
.attr('transform', `translate(0, ${i * 22})`);
g.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('fill', metricColors[key])
.attr('rx', 3);
g.append('text')
.attr('x', 22)
.attr('y', 12)
.attr('fill', '#b0b0b0')
.attr('font-size', '12px')
.text(label);
});
}
// Radar Chart for Provider Comparison
function renderRadarChart() {
const container = d3.select('#radar-chart');
if (container.empty()) return;
const width = container.node().getBoundingClientRect().width;
const height = 400;
const margin = 100;
const radius = Math.min(width, height) / 2 - margin;
container.html('');
const svg = container.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', `translate(${width/2},${height/2})`);
const categories = ['Docker Support', 'Precio', 'Rendimiento', 'Escalabilidad', 'Soporte', 'Facilidad'];
const angleSlice = Math.PI * 2 / categories.length;
const providers = [
{ name: 'DigitalOcean', values: [95, 80, 85, 90, 85, 95], color: '#2196F3' },
{ name: 'Hetzner', values: [85, 95, 92, 80, 65, 75], color: '#4CAF50' },
{ name: 'AWS ECS', values: [100, 50, 90, 100, 90, 60], color: '#FF9800' },
{ name: 'Google Cloud', values: [95, 60, 88, 95, 85, 70], color: '#9C27B0' }
];
const rScale = d3.scaleLinear()
.domain([0, 100])
.range([0, radius]);
// Grid circles
const levels = 5;
for (let i = 1; i <= levels; i++) {
svg.append('circle')
.attr('r', radius * i / levels)
.attr('fill', 'none')
.attr('stroke', '#3a3a3a')
.attr('stroke-dasharray', '4,4');
svg.append('text')
.attr('x', 5)
.attr('y', -radius * i / levels)
.attr('fill', '#666')
.attr('font-size', '10px')
.text(i * 20);
}
// Axis lines and labels
categories.forEach((cat, i) => {
const angle = angleSlice * i - Math.PI / 2;
svg.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', radius * Math.cos(angle))
.attr('y2', radius * Math.sin(angle))
.attr('stroke', '#444');
const labelRadius = radius + 25;
svg.append('text')
.attr('x', labelRadius * Math.cos(angle))
.attr('y', labelRadius * Math.sin(angle))
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'middle')
.attr('fill', '#b0b0b0')
.attr('font-size', '11px')
.text(cat);
});
// Radar areas
const radarLine = d3.lineRadial()
.radius(d => rScale(d))
.angle((d, i) => angleSlice * i)
.curve(d3.curveLinearClosed);
providers.forEach((provider, idx) => {
const path = svg.append('path')
.datum(provider.values)
.attr('d', radarLine)
.attr('fill', provider.color)
.attr('fill-opacity', 0)
.attr('stroke', provider.color)
.attr('stroke-width', 2)
.attr('stroke-opacity', 0);
path.transition()
.duration(800)
.delay(idx * 200)
.attr('fill-opacity', 0.15)
.attr('stroke-opacity', 1);
// Data points
provider.values.forEach((val, i) => {
const angle = angleSlice * i - Math.PI / 2;
svg.append('circle')
.attr('cx', rScale(val) * Math.cos(angle))
.attr('cy', rScale(val) * Math.sin(angle))
.attr('r', 0)
.attr('fill', provider.color)
.transition()
.duration(500)
.delay(idx * 200 + i * 50)
.attr('r', 4);
});
});
// Legend
const legendX = width > 600 ? radius + 50 : -radius;
const legendY = width > 600 ? -radius + 20 : radius + 30;
providers.forEach((provider, idx) => {
const g = svg.append('g')
.attr('transform', `translate(${legendX}, ${legendY + idx * 25})`);
g.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('fill', provider.color)
.attr('rx', 3);
g.append('text')
.attr('x', 22)
.attr('y', 12)
.attr('fill', '#b0b0b0')
.attr('font-size', '12px')
.text(provider.name);
});
}
// شجرة القرار
function renderDecisionTree() {
const container = d3.select('#decision-tree');
if (container.empty()) return;
const width = container.node().getBoundingClientRect().width;
const height = 500;
container.html('');
const svg = container.append('svg')
.attr('width', width)
.attr('height', height);
const g = svg.append('g')
.attr('transform', `translate(50,30)`);
const treeData = {
name: 'Usuarios concurrentes?',
description: 'Cuantos usuarios acceden simultaneamente',
children: [
{
name: '< 50 usuarios',
description: 'Pequena/mediana empresa',
children: [
{
name: 'Equipo DevOps?',
children: [
{ name: 'نعم', children: [
{ name: 'Docker Compose', description: 'نعمmple, efectivo, facil de mantener' }
]},
{ name: 'لا', children: [
{ name: 'Hosting Gestionado', description: 'Odoo4projects o similar' }
]}
]
}
]
},
{
name: '50-200 usuarios',
description: 'Empresa mediana',
children: [
{
name: 'Alta disponibilidad?',
children: [
{ name: 'نعم', children: [
{ name: 'Docker Swarm', description: 'HA simple, rolling updates' }
]},
{ name: 'لا', children: [
{ name: 'Docker + Replicas', description: 'Compose con multiples instancias' }
]}
]
}
]
},
{
name: '> 200 usuarios',
description: 'Gran empresa',
children: [
{ name: 'Kubernetes', description: 'Escalabilidad maxima, service mesh' }
]
}
]
};
const treeLayout = d3.tree()
.size([width - 100, height - 80]);
const root = d3.hierarchy(treeData);
treeLayout(root);
// Links
g.selectAll('.link')
.data(root.links())
.join('path')
.attr('class', 'link')
.attr('d', d3.linkVertical()
.x(d => d.x)
.y(d => d.y))
.attr('fill', 'none')
.attr('stroke', '#555')
.attr('stroke-width', 2)
.attr('stroke-dasharray', function() {
return this.getإجماليLength();
})
.attr('stroke-dashoffset', function() {
return this.getإجماليLength();
})
.transition()
.duration(1200)
.attr('stroke-dashoffset', 0);
// لاdes
const nodes = g.selectAll('.node')
.data(root.descendants())
.join('g')
.attr('class', 'node')
.attr('transform', d => `translate(${d.x},${d.y})`)
.style('opacity', 0)
.style('cursor', 'pointer');
nodes.append('circle')
.attr('r', d => d.children ? 10 : 8)
.attr('fill', d => d.children ? '#2196F3' : '#4CAF50')
.attr('stroke', '#fff')
.attr('stroke-width', 2);
nodes.append('text')
.attr('dy', d => d.children ? -18 : 25)
.attr('text-anchor', 'middle')
.attr('fill', '#e0e0e0')
.attr('font-size', d => d.depth === 0 ? '13px' : '11px')
.attr('font-weight', d => d.depth === 0 ? '600' : '400')
.text(d => d.data.name);
nodes.on('mouseover', function(event, d) {
d3.select(this).select('circle').attr('r', d.children ? 14 : 12);
if (d.data.description) {
tooltip.transition().duration(200).style('opacity', 1);
tooltip.html(`<strong>${d.data.name}</strong><br>${d.data.description}`)
.style('left', (event.pageX + 15) + 'px')
.style('top', (event.pageY - 28) + 'px');
}
})
.on('mouseout', function(event, d) {
d3.select(this).select('circle').attr('r', d.children ? 10 : 8);
tooltip.transition().duration(200).style('opacity', 0);
});
nodes.transition()
.duration(500)
.delay((d, i) => i * 80)
.style('opacity', 1);
}
// Initialize charts when visible
const chartObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.id;
setTimeout(() => {
if (id === 'architecture-diagram') renderArchitectureDiagram();
if (id === 'performance-chart') renderPerformanceChart();
if (id === 'radar-chart') renderRadarChart();
if (id === 'decision-tree') renderDecisionTree();
}, 100);
chartObserver.unobserve(entry.target);
}
});
}, { threshold: 0.15 });
document.querySelectorAll('.chart-container').forEach(el => chartObserver.observe(el));
// Resize handler
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
renderArchitectureDiagram();
renderPerformanceChart();
renderRadarChart();
renderDecisionTree();
}, 300);
});
</script>
</body>
</html>