Image Converter Configuration Guide
The image converter is a Node.js microservice that renders SVG venue backgrounds into optimized WebP images. It produces three variants per background – full resolution, a 1000px preview, and a 400px blurred placeholder with inline base64 data for progressive loading. Images are stored in S3-compatible object storage or on the local filesystem.
Stack: Node.js 22, Express 5, Puppeteer (headless Chromium for SVG rendering), Sharp (image processing), optional Redis (async result queue).
Core settings
| Setting | Type | Default | Description |
|---|---|---|---|
| PORT | integer | 3000 | Express server listening port |
| NODE_ENV | string | production | Node environment |
| DEBUG | boolean | false | Enable debug mode (sets LOG_LEVEL to debug) |
| LOG_LEVEL | string | info | Winston log level: error, warn, info, http, verbose, debug |
File handling settings
| Setting | Type | Default | Description |
|---|---|---|---|
| UPLOAD_FOLDER | string | /tmp | Temporary directory for file uploads (cleaned up after processing) |
| MAX_CONTENT_LENGTH | integer | 16777216 | Maximum upload file size in bytes (default 16 MB) |
Image processing settings
| Setting | Type | Default | Description |
|---|---|---|---|
| MAX_IMAGE_WIDTH | integer | 15000 | Maximum allowed output image width in pixels |
| MAX_IMAGE_HEIGHT | integer | 15000 | Maximum allowed output image height in pixels |
| DEFAULT_WIDTH | integer | 440 | Default width for basic conversion endpoint |
| DEFAULT_HEIGHT | integer | 246 | Default height for basic conversion endpoint |
| PNG_OPTIMIZATION | boolean | false | Enable image compression |
| PNG_OPTIMIZATION_QUALITY | string | 0.6-0.8 | Quality range for WebP output |
Storage settings
| Setting | Type | Default | Description |
|---|---|---|---|
| STORAGE_TYPE | string | s3 | Storage backend: s3 or local |
| LOCAL_STORAGE_PATH | string | ./storage | Filesystem path when using local storage |
AWS S3 configuration
These settings are required when STORAGE_TYPE=s3.
| Setting | Type | Default | Description |
|---|---|---|---|
| AWS_ACCESS_KEY_ID | string | (required) | IAM access key for S3 authentication |
| AWS_SECRET_ACCESS_KEY | string | (required) | IAM secret key for S3 authentication |
| AWS_REGION | string | us-east-1 | AWS region for the S3 bucket |
| AWS_BUCKET_NAME | string | (required) | S3 bucket name for storing images |
| AWS_ENDPOINT_URL | string | Custom S3 endpoint for S3-compatible providers (MinIO, DigitalOcean Spaces, Garage) | |
| AWS_PUBLIC_BASE_URL | string | Override public URL base for generated image URLs (see below) |
ImportantAWS credentials should be provided through secure environment variables or Kubernetes secrets. Never commit credentials to version control.
Public URL resolution
The converter generates public URLs for uploaded images. The URL base is resolved in this priority order:
AWS_PUBLIC_BASE_URL(if set) – used as-is. Example:https://bucket.nyc3.digitaloceanspaces.comAWS_ENDPOINT_URL(derived) – path-style:{endpoint}/{bucket}. Example:https://storage.yandexcloud.net/my-bucket- Default – AWS virtual-hosted style:
https://{bucket}.s3.{region}.amazonaws.com
Self-hosted customers using non-AWS S3 providers (DigitalOcean Spaces, MinIO, Garage, Yandex Cloud) should set AWS_PUBLIC_BASE_URL to ensure correct public URLs.
S3 bucket policy
To allow public read access to images, apply the following bucket policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
CORS configuration
To prevent tainted canvas issues when processing images in web browsers, configure CORS on the bucket:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
NoteFor production environments, restrict
AllowedOriginsto your specific domains instead of*.
Redis settings
Redis is optional. When enabled, conversion results are pushed to a Redis queue for async processing by other services.
| Setting | Type | Default | Description |
|---|---|---|---|
| REDIS_ENABLED | boolean | false | Enable Redis result queue |
| REDIS_HOST | string | localhost | Redis server hostname |
| REDIS_PORT | integer | 6379 | Redis server port |
| REDIS_PASSWORD | string | Redis password (if authentication is required) | |
| REDIS_QUEUE_NAME | string | conversion_results | Queue name for storing conversion results |
If Redis is disabled or unavailable, conversions still succeed – queue publishing is non-blocking.
API endpoints
| Method | Path | Description |
|---|---|---|
| POST | / |
Basic SVG to image conversion. Returns the image file directly. Query params: width, height |
| POST | /background/ |
Full background conversion. Returns JSON with three variant URLs (full, preview, blurred). Query params: filename, debug |
| POST | /thumbnail/ |
Thumbnail conversion. Returns JSON with three variant URLs. Query params: filename, debug |
| GET | /health |
Service health check. Returns { "status": "healthy" } |
| GET | /health/redis |
Redis connection status and queue length |
| GET | /metrics |
Prometheus metrics in text format |
All POST endpoints accept multipart/form-data with an SVG file. Files must have .svg extension and image/svg+xml MIME type.
Rate limiting: 30 requests per 60 seconds per IP address.
Request timeout: 5 minutes (300 seconds).
Output format
The converter generates WebP images (not PNG), providing 30-50% smaller file sizes with alpha channel support.
Three variants are produced for each background:
| Variant | Width | Quality | Purpose |
|---|---|---|---|
| full | Original | 60-80% | Full-resolution venue background |
| preview | 1000px | 60-80% | Quick-loading preview for initial render |
| blurred | 400px | 30-50% | Placeholder with inline base64 data for progressive loading |
Files are stored at backgrounds/{filename}/{variant}.webp in the configured storage backend.
Docker
The service runs in a Docker container based on node:22.14.0-alpine with Chromium installed for Puppeteer.
Port: 3000
Health check: GET /health every 30 seconds (3 retries, 5-second start period)
Environment variables for Puppeteer:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true(uses system Chromium)PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
Security: Runs as non-root user (uid 10001). Uses tini as init process to prevent zombie processes.
Monitoring
Prometheus metrics
The /metrics endpoint exposes:
http_request_duration_seconds– histogram with method, route, status_code labelshttp_requests_total– counter with method, route, status_code labels- Default Node.js process metrics (memory, CPU, event loop lag, GC)
Metric buckets are optimized for image processing workloads: 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30 seconds.
Health endpoints
GET /health– returns 200 if the service is runningGET /health/redis– returns Redis connection status, queue name, and queue length
Configuration methods
The application can be configured through multiple methods listed in order of precedence:
- Environment variables
- Kubernetes ConfigMap/Secrets
.envfile (development only)
Kubernetes deployment
When deploying to Kubernetes, configuration is managed through Helm values and secrets:
converter:
image:
repository: registry.gitlab.com/seatmap.pro/seatmap/converter-service
tag: latest
env:
PORT: "3000"
LOG_LEVEL: "info"
STORAGE_TYPE: "s3"
AWS_REGION: "us-east-1"
AWS_BUCKET_NAME: "your-bucket"
# AWS_PUBLIC_BASE_URL: "https://your-cdn.example.com" # if using non-AWS S3
Secrets management
AWS credentials and Redis passwords should be managed securely using one of these methods:
- Kubernetes Secrets (injected via
seatmap-helm-secrets) - External Secrets Operator
- IAM roles for Service Accounts (IRSA)
Development setup
For local development using Docker Compose:
cd products/converter-service
cp .env.sample .env
# Edit .env with your settings (use STORAGE_TYPE=local for development)
docker compose up
This starts the converter service on port 3000 and a Redis instance on port 6379.
Never commit sensitive credentials to version control.
Troubleshooting
Common configuration issues:
- Image upload fails: Check
MAX_CONTENT_LENGTHand ingress body-size configuration. The default 16 MB limit may need increasing for large SVG files. - S3 access denied: Verify AWS credentials and bucket permissions. Check that
AWS_PUBLIC_BASE_URLmatches your actual bucket URL scheme. - Blank or corrupted images: Verify Chromium is installed (
/usr/bin/chromium-browser). In Docker, ensure thePUPPETEER_EXECUTABLE_PATHenvironment variable is set correctly. - High memory usage: Sharp and Puppeteer can consume significant memory with large SVG files. Set container memory limits to at least 512 MB, with 2 GB recommended for production.
- WebP not loading in renderer: Ensure CORS headers are configured on the S3 bucket. The renderer needs cross-origin access to load background images.
- Wrong public URLs: If using a non-AWS S3 provider, set
AWS_PUBLIC_BASE_URLexplicitly. The default URL generation assumes AWS virtual-hosted bucket style.