# SQL Server 2017 Express - Low RAM Edition 🐳

[![Docker Pulls](https://img.shields.io/docker/pulls/andresgarcia0313/mssql-express-lowram)](https://hub.docker.com/r/andresgarcia0313/mssql-express-lowram)
[![Docker Image Size](https://img.shields.io/docker/image-size/andresgarcia0313/mssql-express-lowram/latest)](https://hub.docker.com/r/andresgarcia0313/mssql-express-lowram)

Imagen de Docker optimizada para ejecutar **SQL Server 2017 Express** con **bajo consumo de RAM** (384MB - 512MB).

## ✨ Características

- 🎯 **RAM mínima:** 384MB (vs 2GB oficial de Microsoft)
- ⚡ **Optimizaciones automáticas** al iniciar el contenedor
- 🔧 **Trace flags** preconfigurados para estabilidad
- 🏥 **Health check** integrado
- 📝 **Logs informativos** del proceso de optimización
- 🔐 **Contraseña configurable** por variable de entorno

## 🚀 Uso Rápido

```bash
docker run -d \
  --name mssql \
  --memory=384m \
  --memory-swap=2g \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -p 1433:1433 \
  andresgarcia0313/mssql-express-lowram
```

---

## 📋 Variables de Entorno

### Variables Requeridas

| Variable | Descripción |
|----------|-------------|
| `MSSQL_SA_PASSWORD` | **REQUERIDO.** Password del usuario SA (administrador) |

### Variables Opcionales

| Variable | Default | Descripción |
|----------|---------|-------------|
| `MSSQL_PID` | `Express` | Edición de SQL Server |
| `MSSQL_MEMORY_LIMIT_MB` | `340` | Límite de memoria interna de SQL Server (MB) |
| `MSSQL_COLLATION` | `SQL_Latin1_General_CP1_CI_AS` | Collation de la instancia |
| `MSSQL_LCID` | `1033` | ID de idioma (1033=English, 3082=Spanish) |
| `MSSQL_TCP_PORT` | `1433` | Puerto TCP de SQL Server |
| `TZ` | `UTC` | Zona horaria del contenedor |
| `ACCEPT_EULA` | `Y` | Ya aceptada en la imagen |

### Valores de MSSQL_PID

| Valor | Descripción | Límites |
|-------|-------------|---------|
| `Express` | **Recomendado.** Gratuito para desarrollo y producción | 1GB RAM, 10GB DB |
| `Developer` | Todas las características, solo desarrollo | Sin límites |
| `Standard` | Requiere licencia | 128GB RAM |
| `Enterprise` | Requiere licencia | Sin límites |

### Requisitos del Password

El password de SA debe cumplir:
- Mínimo **8 caracteres**
- Al menos una letra **mayúscula** (A-Z)
- Al menos una letra **minúscula** (a-z)
- Al menos un **número** (0-9)
- Al menos un **caracter especial** (!@#$%^&*)

**Ejemplos válidos:** `MiPassword123!`, `Secure@Pass99`, `Dev#Server2024`

---

## 💾 Configuración de Memoria y Swap

### Parámetros de Docker para Memoria

| Parámetro | Descripción |
|-----------|-------------|
| `--memory` | Límite de RAM física para el contenedor |
| `--memory-swap` | Límite total (RAM + Swap). Debe ser mayor que `--memory` |
| `--memory-reservation` | Reserva mínima de RAM (soft limit) |
| `--memory-swappiness` | Preferencia de uso de swap (0-100) |
| `--oom-kill-disable` | Deshabilita OOM killer (usar con cuidado) |

### Configuración Recomendada por Tipo de Disco

#### 💿 Para SSD/NVMe (Recomendado)

El swap en SSD es rápido, permite configuración más agresiva:

```bash
docker run -d \
  --name mssql \
  --memory=384m \
  --memory-swap=4g \
  --memory-reservation=256m \
  --memory-swappiness=70 \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -e MSSQL_PID=Express \
  -e MSSQL_MEMORY_LIMIT_MB=340 \
  -p 1433:1433 \
  -v mssql_data:/var/opt/mssql/data \
  andresgarcia0313/mssql-express-lowram
```

| Parámetro | Valor | Razón |
|-----------|-------|-------|
| `--memory` | 384m | RAM mínima viable |
| `--memory-swap` | 4g | Swap generoso (SSD es rápido) |
| `--memory-swappiness` | 60 | Balance entre RAM y swap |

#### 🗄️ Para HDD (Disco Mecánico)

El swap en HDD es lento, minimizar su uso:

```bash
docker run -d \
  --name mssql \
  --memory=512m \
  --memory-swap=1g \
  --memory-reservation=384m \
  --memory-swappiness=10 \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -e MSSQL_PID=Express \
  -e MSSQL_MEMORY_LIMIT_MB=450 \
  -p 1433:1433 \
  -v mssql_data:/var/opt/mssql/data \
  andresgarcia0313/mssql-express-lowram
```

| Parámetro | Valor | Razón |
|-----------|-------|-------|
| `--memory` | 512m | Más RAM para evitar swap |
| `--memory-swap` | 1g | Swap mínimo (HDD lento) |
| `--memory-swappiness` | 10 | Evitar swap excepto emergencia |

### Tabla de Configuraciones Recomendadas

| Escenario | RAM | Swap | Swappiness | MSSQL_MEMORY_LIMIT_MB |
|-----------|-----|------|------------|----------------------|
| **Mínimo SSD** | 384m | 4g | 60 | 340 |
| **Mínimo HDD** | 512m | 1g | 10 | 450 |
| **Desarrollo** | 512m | 2g | 30 | 450 |
| **CI/CD** | 512m | 2g | 30 | 450 |
| **Múltiples DBs** | 768m | 2g | 20 | 680 |
| **Carga moderada** | 1g | 2g | 10 | 900 |

---

## ⚡ Configuración para Máximo Rendimiento

### Desarrollo Local - Máximo Rendimiento

```bash
docker run -d \
  --name mssql-performance \
  --memory=1g \
  --memory-swap=2g \
  --memory-swappiness=10 \
  --cpus=2 \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -e MSSQL_PID=Developer \
  -e MSSQL_MEMORY_LIMIT_MB=900 \
  -e TZ=America/Bogota \
  -p 1433:1433 \
  -v mssql_data:/var/opt/mssql/data \
  -v mssql_log:/var/opt/mssql/log \
  andresgarcia0313/mssql-express-lowram
```

### Máxima Estabilidad (Producción-like)

```bash
docker run -d \
  --name mssql-stable \
  --memory=768m \
  --memory-swap=2g \
  --memory-reservation=512m \
  --memory-swappiness=20 \
  --restart=unless-stopped \
  --cpus=2 \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -e MSSQL_PID=Express \
  -e MSSQL_MEMORY_LIMIT_MB=680 \
  -p 1433:1433 \
  -v /path/to/data:/var/opt/mssql/data \
  -v /path/to/log:/var/opt/mssql/log \
  -v /path/to/backup:/var/opt/mssql/backup \
  andresgarcia0313/mssql-express-lowram
```

### Recursos Mínimos Absolutos

```bash
docker run -d \
  --name mssql-minimal \
  --memory=384m \
  --memory-swap=6g \
  --memory-swappiness=80 \
  -e MSSQL_SA_PASSWORD=MiPassword123! \
  -e MSSQL_PID=Express \
  -e MSSQL_MEMORY_LIMIT_MB=300 \
  -p 1433:1433 \
  andresgarcia0313/mssql-express-lowram
```

> ⚠️ Con recursos mínimos, el rendimiento dependerá mucho del swap (requiere SSD).

---

## 🔧 Optimizaciones Incluidas en la Imagen

### Configuración de SQL Server (Automática)

| Setting | Valor | Propósito |
|---------|-------|-----------|
| `max server memory` | 320 MB | Límite del buffer pool |
| `min server memory` | 128 MB | Permite liberar RAM bajo presión |
| `max degree of parallelism` | 2 | Reduce consumo en queries paralelas |
| `cost threshold for parallelism` | 50 | Solo queries costosas usan paralelismo |
| `optimize for ad hoc workloads` | ON | Reduce caché de planes únicos |
| `recovery interval` | 1 min | Recuperación rápida tras crash |

### Trace Flags Activos (Automáticos)

| Flag | Propósito |
|------|-----------|
| **TF 1117** | Crecimiento uniforme de archivos de datos |
| **TF 1118** | Reduce contención en tempdb (extents uniformes) |
| **TF 3226** | Suprime logs de backup exitoso (menos I/O) |

---

## 📦 Docker Compose

### Configuración Básica

```yaml
version: '3.8'

services:
  sqlserver:
    image: andresgarcia0313/mssql-express-lowram
    container_name: mssql
    mem_limit: 512m
    memswap_limit: 2g
    mem_reservation: 384m
    restart: unless-stopped
    environment:
      - MSSQL_SA_PASSWORD=MiPassword123!
      - MSSQL_PID=Express
      - MSSQL_MEMORY_LIMIT_MB=450
      - TZ=America/Bogota
    ports:
      - "1433:1433"
    volumes:
      - mssql_data:/var/opt/mssql/data
      - mssql_log:/var/opt/mssql/log
    healthcheck:
      test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -Q "SELECT 1"
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  mssql_data:
  mssql_log:
```

### Configuración Avanzada (SSD + Rendimiento)

```yaml
version: '3.8'

services:
  sqlserver:
    image: andresgarcia0313/mssql-express-lowram
    container_name: mssql
    mem_limit: 768m
    memswap_limit: 4g
    mem_reservation: 512m
    cpus: 2
    restart: unless-stopped
    environment:
      MSSQL_SA_PASSWORD: ${MSSQL_PASSWORD:-MiPassword123!}
      MSSQL_PID: Express
      MSSQL_MEMORY_LIMIT_MB: 680
      MSSQL_COLLATION: Modern_Spanish_CI_AS
      TZ: America/Bogota
    ports:
      - "1433:1433"
    volumes:
      - mssql_data:/var/opt/mssql/data
      - mssql_log:/var/opt/mssql/log
      - mssql_backup:/var/opt/mssql/backup
    healthcheck:
      test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -Q "SELECT 1"
      interval: 30s
      timeout: 10s
      start_period: 30s
      retries: 3
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  mssql_data:
    driver: local
  mssql_log:
    driver: local
  mssql_backup:
    driver: local
```

---

## 🔌 Conexión

### Desde línea de comandos

```bash
# Usando sqlcmd dentro del contenedor
docker exec -it mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P 'MiPassword123!'

# Usando sqlcmd local
sqlcmd -S localhost,1433 -U sa -P 'MiPassword123!'
```

### Connection Strings

**.NET / C#**
```csharp
Server=localhost,1433;Database=MiDB;User Id=sa;Password=MiPassword123!;TrustServerCertificate=True;
```

**Node.js (mssql)**
```javascript
const config = {
  server: 'localhost',
  port: 1433,
  user: 'sa',
  password: 'MiPassword123!',
  database: 'MiDB',
  options: { trustServerCertificate: true }
};
```

**Python (pyodbc)**
```python
conn_str = (
    "DRIVER={ODBC Driver 17 for SQL Server};"
    "SERVER=localhost,1433;"
    "DATABASE=MiDB;"
    "UID=sa;"
    "PWD=MiPassword123!;"
    "TrustServerCertificate=yes;"
)
```

**Java (JDBC)**
```java
String url = "jdbc:sqlserver://localhost:1433;database=MiDB;user=sa;password=MiPassword123!;trustServerCertificate=true;";
```

---

## 🏗️ Ejemplos de Uso

### CI/CD (GitHub Actions)

```yaml
services:
  sqlserver:
    image: andresgarcia0313/mssql-express-lowram
    env:
      MSSQL_SA_PASSWORD: TestPassword123!
      MSSQL_PID: Express
      MSSQL_MEMORY_LIMIT_MB: 450
    ports:
      - 1433:1433
    options: >-
      --memory=512m
      --memory-swap=2g
      --health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P TestPassword123! -Q 'SELECT 1'"
      --health-interval=10s
      --health-timeout=5s
      --health-retries=5
```

### Kubernetes

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: mssql-lowram
spec:
  containers:
  - name: mssql
    image: andresgarcia0313/mssql-express-lowram
    resources:
      limits:
        memory: "768Mi"
      requests:
        memory: "512Mi"
    env:
    - name: MSSQL_SA_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mssql-secret
          key: sa-password
    - name: MSSQL_PID
      value: "Express"
    - name: MSSQL_MEMORY_LIMIT_MB
      value: "680"
    ports:
    - containerPort: 1433
    livenessProbe:
      exec:
        command:
        - /opt/mssql-tools/bin/sqlcmd
        - -S
        - localhost
        - -U
        - sa
        - -P
        - $(MSSQL_SA_PASSWORD)
        - -Q
        - SELECT 1
      initialDelaySeconds: 30
      periodSeconds: 30
```

---

## ⚠️ Advertencias

- **No recomendado para producción pesada** - La RAM es insuficiente para cargas grandes
- **Swap en HDD** - Experimentará lentitud significativa, usar SSD cuando sea posible
- **Realizar backups frecuentes** en entornos con recursos limitados
- Los **trace flags** se aplican en cada inicio (no persisten en la imagen)
- `MSSQL_MEMORY_LIMIT_MB` debe ser **menor** que `--memory` (dejar ~60MB para el OS)

---

## 🔍 Troubleshooting

### Ver logs de optimización

```bash
docker logs mssql 2>&1 | grep LOWRAM
```

### Verificar configuración aplicada

```bash
docker exec mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P 'MiPassword123!' \
  -Q "SELECT name, value_in_use FROM sys.configurations WHERE name LIKE '%memory%' OR name LIKE '%parallel%'"
```

### Ver uso de memoria real

```bash
docker stats mssql --no-stream
```

### El contenedor no inicia

1. Verificar que el password cumple los requisitos
2. Verificar que hay suficiente RAM: `docker stats`
3. Revisar logs: `docker logs mssql`
4. Verificar swap disponible: `free -h`

### Rendimiento lento

1. Verificar tipo de disco (SSD vs HDD)
2. Aumentar `--memory` si es posible
3. Reducir `--memory-swappiness` en HDD
4. Verificar que no hay otros contenedores compitiendo por recursos

---

## 🔬 Proceso de Optimización (Trazabilidad)

Esta imagen fue creada mediante un proceso de investigación y optimización documentado. A continuación se detalla el proceso completo para reproducibilidad y trazabilidad.

### Problema Original

Microsoft SQL Server 2017 tiene un **requisito mínimo oficial de 2GB de RAM**, lo cual es excesivo para:
- Desarrollo local en equipos con recursos limitados
- Pipelines de CI/CD
- Contenedores en entornos de pruebas
- Microservicios que solo necesitan una DB pequeña

### Investigación Realizada

1. **Documentación oficial consultada:**
   - [Configure SQL Server Docker Containers](https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-docker-container-configure)
   - [SQL Server Linux Performance Best Practices](https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-performance-best-practices)
   - [KB4347055 - Fix OOM in Docker containers](https://support.microsoft.com/en-us/topic/kb4347055)
   - [mssql-conf Configuration Tool](https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-configure-mssql-conf)

2. **Hallazgos clave:**
   - `MSSQL_MEMORY_LIMIT_MB` existe pero requiere versiones recientes
   - SQL Server 2017 CU10+ incluye fix para respetar límites de contenedor
   - El 80% de RAM por defecto es configurable vía `sp_configure`
   - Trace flags pueden mejorar estabilidad en entornos con poca RAM

### Optimizaciones Aplicadas

#### 1. Configuración de SQL Server (`sp_configure`)

```sql
-- Ejecutado automáticamente al iniciar el contenedor

-- Limitar buffer pool (principal consumidor de RAM)
EXEC sp_configure 'max server memory', 320;

-- Permitir liberar memoria bajo presión del sistema
EXEC sp_configure 'min server memory', 128;

-- Reducir caché de planes para queries ad-hoc (ahorra RAM)
EXEC sp_configure 'optimize for ad hoc workloads', 1;

-- Limitar paralelismo (operaciones paralelas consumen más RAM)
EXEC sp_configure 'max degree of parallelism', 2;

-- Solo queries costosas usan paralelismo
EXEC sp_configure 'cost threshold for parallelism', 50;

-- Checkpoints frecuentes para recuperación rápida
EXEC sp_configure 'recovery interval (min)', 1;
```

#### 2. Trace Flags para Estabilidad

```sql
-- Ejecutados automáticamente al iniciar

-- TF 1117: Crecimiento uniforme de archivos
-- Evita que un archivo crezca más que otros, reduciendo fragmentación
DBCC TRACEON(1117, -1);

-- TF 1118: Extents uniformes en tempdb
-- Reduce contención de páginas mixtas, mejora rendimiento
DBCC TRACEON(1118, -1);

-- TF 3226: Suprime logs de backup exitoso
-- Reduce I/O en el log de errores
DBCC TRACEON(3226, -1);
```

#### 3. Entrypoint Personalizado

```bash
# El entrypoint realiza:
# 1. Valida que MSSQL_SA_PASSWORD esté definido
# 2. Inicia SQL Server en foreground
# 3. En background, espera que SQL Server esté listo
# 4. Aplica todas las optimizaciones automáticamente
# 5. Muestra logs informativos del proceso
```

#### 4. Health Check Integrado

```dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
    CMD /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$MSSQL_SA_PASSWORD" -Q "SELECT 1"
```

### Pruebas de Validación

La imagen fue probada durante **6+ minutos** de monitoreo continuo:

| Minuto | Estado | RAM | Operación |
|--------|--------|-----|-----------|
| 0 | ✅ OK | 380.9 MB | Query básica |
| 2 | ✅ OK | 381.9 MB | Count de databases |
| 4 | ✅ OK | 381.8 MB | CREATE TABLE + 1000 INSERTs |
| 6 | ✅ OK | 381.6 MB | Listado de databases |

**Resultado:** Sin errores, sin OOM, sin crashes, memoria estable.

### Archivos de la Imagen

```
deploy/docker-mssql/
├── Dockerfile          # Imagen base + labels + health check
├── entrypoint.sh       # Validación + inicio + optimización automática
├── optimize.sql        # Queries de sp_configure y trace flags
├── build-push.sh       # Script para build y push a Docker Hub
└── README.md           # Esta documentación
```

### Limitaciones Conocidas

| Limitación | Causa | Mitigación |
|------------|-------|------------|
| RAM mínima ~380MB | Componentes fuera del buffer pool | Usar swap en SSD |
| No persisten trace flags | Se aplican en runtime, no en imagen | Se re-aplican en cada inicio |
| Rendimiento reducido | Menos RAM = menos caché | Usar SSD, aumentar swap |

### Diferencias vs Imagen Oficial

| Aspecto | Imagen Oficial | Esta Imagen |
|---------|----------------|-------------|
| RAM mínima | 2 GB | 384 MB |
| Optimizaciones | Ninguna | Automáticas |
| Trace flags | Ninguno | 3 preconfigurados |
| Health check | No | Sí |
| Validación password | No | Sí (con mensaje de error claro) |
| Documentación | Básica | Completa |

### Reproducir las Optimizaciones Manualmente

Si prefieres aplicar las optimizaciones a la imagen oficial:

```bash
# 1. Iniciar contenedor oficial
docker run -d --name mssql --memory=512m \
  -e ACCEPT_EULA=Y \
  -e MSSQL_SA_PASSWORD=TuPassword123! \
  -p 1433:1433 \
  mcr.microsoft.com/mssql/server:2017-latest

# 2. Esperar que inicie
sleep 30

# 3. Aplicar optimizaciones
docker exec mssql /opt/mssql-tools/bin/sqlcmd \
  -S localhost -U sa -P 'TuPassword123!' -Q "
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'max server memory', 384; RECONFIGURE;
EXEC sp_configure 'min server memory', 128; RECONFIGURE;
EXEC sp_configure 'optimize for ad hoc workloads', 1; RECONFIGURE;
EXEC sp_configure 'max degree of parallelism', 2; RECONFIGURE;
EXEC sp_configure 'cost threshold for parallelism', 50; RECONFIGURE;
EXEC sp_configure 'recovery interval (min)', 1; RECONFIGURE;
DBCC TRACEON(1117, -1);
DBCC TRACEON(1118, -1);
DBCC TRACEON(3226, -1);
"
```

---

## 📊 Basado en

- **Microsoft SQL Server 2017 Express** (CU31 - versión 14.0.3515.1)
- Imagen base: `mcr.microsoft.com/mssql/server:2017-latest`
- Documentación oficial de Microsoft para SQL Server en Linux

## 📄 Licencia

- **SQL Server Express**: Gratuito para desarrollo y producción (con límites)
- **Scripts de optimización**: MIT License

## 👤 Autor

**andresgarcia0313**

- GitHub: [@andresgarcia0313](https://github.com/andresgarcia0313)
- Docker Hub: [andresgarcia0313](https://hub.docker.com/u/andresgarcia0313)

---

⭐ Si esta imagen te fue útil, considera dejar una estrella en el repositorio.
