MJML to HTML rendering API server using Express.js, ready for Docker & production use.
1.1K
Self-hosted MJML rendering API - Convert MJML to responsive HTML emails via HTTP.
docker run -p 15500:15500 mactorient/mjml-server:latest
Test it:
curl -X POST http://localhost:15500/v1/render \
-H "Content-Type: application/json" \
-d '{"mjml": "<mjml><mj-body><mj-section><mj-column><mj-text>Hello World!</mj-text></mj-column></mj-section></mj-body></mjml>"}'
{{name}}, {{email}})Size: ~241MB (Alpine-based, esbuild bundled)
Architectures: linux/amd64, linux/arm64
Base: node:24-alpine
| Endpoint | Method | Description |
|---|---|---|
/v1/render | POST | Render MJML to HTML |
/v1/render/batch | POST | Batch render (max 50) |
/v1/validate | POST | Validate MJML syntax |
/v1/health | GET | Health check |
services:
mjml-server:
image: mactorient/mjml-server:latest
ports:
- '15500:15500'
environment:
# MJML Options
MJML_MINIFY: 'true'
MJML_KEEP_COMMENTS: 'false'
MJML_VALIDATION_LEVEL: 'strict'
# Server Options
MJML_HOST: '0.0.0.0'
MJML_PORT: '15500'
MJML_USE_COMPRESSION: 'true'
# Security (optional)
MJML_AUTH_USER: 'admin'
MJML_AUTH_PASS: 'secret'
MJML_ENABLE_RATE_LIMIT: 'true'
MJML_RATE_LIMIT_MAX: '100'
MJML_RATE_LIMIT_WINDOW: '15'
| Variable | Default | Description |
|---|---|---|
MJML_HOST | 0.0.0.0 | Server host |
MJML_PORT | 15500 | Server port |
MJML_MINIFY | false | Minify HTML output |
MJML_KEEP_COMMENTS | true | Keep MJML comments |
MJML_VALIDATION_LEVEL | soft | strict/soft/skip |
MJML_USE_COMPRESSION | true | Gzip compression |
MJML_MAX_BODY | 1mb | Max request size |
MJML_AUTH_USER | - | HTTP Basic Auth user |
MJML_AUTH_PASS | - | HTTP Basic Auth pass |
MJML_ENABLE_RATE_LIMIT | false | Enable rate limiting |
MJML_RATE_LIMIT_MAX | 100 | Max requests per window |
MJML_RATE_LIMIT_WINDOW | 15 | Window in minutes |
curl -X POST http://localhost:15500/v1/render \
-H "Content-Type: application/json" \
-d '{
"mjml": "<mjml><mj-body>...</mj-body></mjml>"
}'
curl -X POST http://localhost:15500/v1/render \
-H "Content-Type: application/json" \
-d '{
"mjml": "<mjml><mj-body><mj-text>Hello {{name}}!</mj-text></mj-body></mjml>",
"variables": {
"name": "John Doe",
"email": "[email protected]"
}
}'
curl -X POST http://localhost:15500/v1/render/batch \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"mjml": "<mjml>...</mjml>",
"variables": {"name": "Alice"}
},
{
"mjml": "<mjml>...</mjml>",
"variables": {"name": "Bob"}
}
]
}'
curl -X POST http://localhost:15500/v1/validate \
-H "Content-Type: application/json" \
-d '{
"mjml": "<mjml><mj-body>...</mj-body></mjml>"
}'
version: '3.8'
services:
mjml-server:
image: mactorient/mjml-server:latest
restart: unless-stopped
ports:
- '15500:15500'
environment:
MJML_MINIFY: 'true'
MJML_ENABLE_RATE_LIMIT: 'true'
MJML_RATE_LIMIT_MAX: '200'
healthcheck:
test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://localhost:15500/v1/health']
interval: 30s
timeout: 3s
retries: 3
start_period: 5s
services:
mjml-server:
image: mactorient/mjml-server:latest
ports:
- '15500:15500'
environment:
# Enable authentication
MJML_AUTH_USER: '${MJML_USER}'
MJML_AUTH_PASS: '${MJML_PASS}'
# Enable rate limiting
MJML_ENABLE_RATE_LIMIT: 'true'
MJML_RATE_LIMIT_MAX: '100'
# Strict validation
MJML_VALIDATION_LEVEL: 'strict'
# Resource limits
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
# Check health
curl http://localhost:15500/v1/health
# Response
{
"status": "ok",
"message": "MJML Render API is healthy",
"mjml_version": "^4.16.1",
"options": {
"keepComments": true,
"beautify": false,
"minify": false,
"validationLevel": "soft"
}
}
node:24-alpinePort already in use?
docker run -p 8080:15500 mactorient/mjml-server:latest
Need more memory?
docker run -m 512m mactorient/mjml-server:latest
Check logs:
docker logs <container-id>
MIT License - see LICENSE
For information, visit https://github.com/mertemr/mjml-server#readme
Content type
Image
Digest
sha256:5b3cdedb2…
Size
58.3 MB
Last updated
9 months ago
docker pull mactorient/mjml-server