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:

  1. AWS_PUBLIC_BASE_URL (if set) – used as-is. Example: https://bucket.nyc3.digitaloceanspaces.com
  2. AWS_ENDPOINT_URL (derived) – path-style: {endpoint}/{bucket}. Example: https://storage.yandexcloud.net/my-bucket
  3. 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 AllowedOrigins to 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 labels
  • http_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 running
  • GET /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:

  1. Environment variables
  2. Kubernetes ConfigMap/Secrets
  3. .env file (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_LENGTH and 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_URL matches your actual bucket URL scheme.
  • Blank or corrupted images: Verify Chromium is installed (/usr/bin/chromium-browser). In Docker, ensure the PUPPETEER_EXECUTABLE_PATH environment 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_URL explicitly. The default URL generation assumes AWS virtual-hosted bucket style.