Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Nexrender
https://github.com/inlife/nexrender
Admin
Nexrender automates Adobe After Effects rendering workflows, enabling data-driven and template-based
...
Tokens:
132,149
Snippets:
907
Trust Score:
8.6
Update:
4 hours ago
Context
Skills
Chat
Benchmark
88.3
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# nexrender nexrender is an open-source Node.js automation framework for Adobe After Effects rendering workflows. It enables data-driven, template-based video generation by wrapping Adobe's `aerender` CLI binary and orchestrating the full lifecycle of a render job: asset downloading, script injection, rendering, post-processing, and delivery. The project is structured as a Lerna monorepo with cleanly separated packages for the rendering core, job server, worker, API client, and a rich ecosystem of action plugins and cloud storage providers. The core concept is a **Job** — a JSON document that declares an AE project template (`.aep`), a composition to render, a list of assets to inject (images, video, audio, data values, or JSX scripts), and an ordered pipeline of actions to run before and after rendering. Jobs can be executed locally with `nexrender-cli`, or distributed across a render farm via `nexrender-server` and `nexrender-worker`. The modular plugin architecture allows custom actions to be inserted at any stage of the pipeline, and external cloud providers (AWS S3, Google Cloud Storage, FTP, SFTP) can supply and receive assets transparently through a URI protocol scheme. --- ## @nexrender/core — `init(settings)` Initializes the render environment: auto-detects the `aerender` binary, applies the command-line renderer patch, optionally creates a free-use license file, and returns a frozen settings object that is passed to every subsequent render call. Must be called before `render()` when fine-grained configuration is needed; `render()` calls it automatically if passed raw options. ```js const { init, render } = require('@nexrender/core'); const settings = init({ binary: '/Applications/Adobe After Effects 2024/aerender', workpath: '/tmp/nexrender-jobs', addLicense: true, // create ae_render_only_node.txt (free trial use) skipCleanup: false, // delete temp folder after render debug: false, multiFrames: true, // use aerender multi-frame rendering multiFramesCPU: 80, // % CPU for multi-frame (default 90) maxMemoryPercent: 60, imageCachePercent: 40, maxRenderTimeout: 300, // abort after 300 s noAnalytics: true, reuse: false, }); // settings.__initialized === true // settings.binary === resolved absolute path // settings.workpath === '/tmp/nexrender-jobs' console.log('AE binary:', settings.binary); ``` --- ## @nexrender/core — `render(jobConfig, settings?)` Executes the full rendering pipeline for a single job: setup → predownload → download → postdownload → prerender → script → dorender → postrender → cleanup. Returns a Promise that resolves to the completed job object. If `settings` is not yet initialized, `init()` is invoked internally. ```js const { render } = require('@nexrender/core'); const job = { template: { src: 'https://example.com/assets/project.aep', composition: 'main', outputModule: 'H.264 - Match Render Settings - 15 Mbps', outputExt: 'mp4', frameStart: 0, frameEnd: 150, continueOnMissing: true, }, assets: [ { src: 'https://example.com/assets/logo.png', type: 'image', layerName: 'logo.png', }, { type: 'data', layerName: 'title', property: 'Source Text', value: 'Hello World', }, ], actions: { postrender: [ { module: '@nexrender/action-encode', preset: 'mp4', output: 'result.mp4' }, { module: '@nexrender/action-copy', input: 'result.mp4', output: '/mnt/shared/renders/result.mp4' }, ], }, onChange: (job) => console.log(`[${job.uid}] state changed to: ${job.state}`), onRenderProgress: (job, percent) => console.log(`[${job.uid}] render progress: ${percent}%`), onRenderError: (job, err) => console.error(`[${job.uid}] render error: ${err.message}`), }; render(job, { workpath: '/tmp/nexrender', debug: true }) .then(completedJob => { console.log('Render finished:', completedJob.output); // completedJob.state === 'finished' // completedJob.output === '/tmp/nexrender/<uid>/result.mp4' (after action-copy moves it) }) .catch(err => { console.error('Render failed:', err.message); process.exit(1); }); ``` --- ## @nexrender/server — `listen(port, secret, callback?)` Starts the nexrender HTTP API server on the specified port, optionally protected by a shared secret that every client must send as the `nexrender-secret` request header. The server manages a job queue backed by a file-based or Redis database and exposes a REST API under `/api/v1`. ```js const { listen } = require('@nexrender/server'); const server = listen(3050, 'my-super-secret', () => { console.log('nexrender-server listening on port 3050'); }); // Or with environment-variable-based Redis backend: // NEXRENDER_DATABASE_PROVIDER=redis REDIS_URL=redis://localhost:6379 node server.js // Programmatic shutdown: // server.close(); ``` ```bash # Binary usage nexrender-server --port=3050 --secret=my-super-secret # With custom database path NEXRENDER_DATABASE=/etc/nexrender/db.json nexrender-server -p 3050 # Cleanup all stored jobs nexrender-server --cleanup ``` --- ## @nexrender/server — REST API endpoints The server exposes a complete REST API for job lifecycle management. All endpoints require the `nexrender-secret` header when the server is started with a secret. The `GET /api/v1/health` endpoint is always open. ```bash # Health check (no auth required) curl http://localhost:3050/api/v1/health # Create a new job curl -X POST http://localhost:3050/api/v1/jobs \ -H "nexrender-secret: my-super-secret" \ -H "content-type: application/json" \ -d '{ "template": { "src": "http://example.com/project.aep", "composition": "main" }, "assets": [ { "src": "http://example.com/bg.jpg", "type": "image", "layerName": "background.jpg" } ], "tags": "priority,gpu", "priority": 10 }' # Response: { "uid": "V1StGXR8Z5jdHi6B", "state": "queued", ... } # List all jobs curl -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs # Fetch a specific job curl -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs/V1StGXR8Z5jdHi6B # Poll job status only (lightweight) curl -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs/V1StGXR8Z5jdHi6B/status # Response: { "uid": "...", "state": "render:dorender", "renderProgress": 42 } # Worker pickup (with optional tag filtering) curl -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs/pickup curl -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs/pickup/priority,gpu # Update a job curl -X PUT http://localhost:3050/api/v1/jobs/V1StGXR8Z5jdHi6B \ -H "nexrender-secret: my-super-secret" \ -H "content-type: application/json" \ -d '{ "state": "queued" }' # Remove a job curl -X DELETE -H "nexrender-secret: my-super-secret" http://localhost:3050/api/v1/jobs/V1StGXR8Z5jdHi6B ``` --- ## @nexrender/api — `createClient(options)` Creates a JavaScript API client for communicating with a `nexrender-server`. Returns an object with methods for all job operations. The `addJob()` method returns an EventEmitter that fires `created`, `started`, `progress`, `finished`, and `error` events as the server polls for state changes. ```js const { createClient } = require('@nexrender/api'); const client = createClient({ host: 'http://my.server.com:3050', secret: 'my-super-secret', polling: 5000, // ms between status checks (default: 10000) name: 'client-01', headers: { 'x-custom-header': 'value', // Dynamic header (resolved before each request): 'x-auth-token': async () => await getTokenFromVault(), }, }); // Add a new job and subscribe to events const emitter = await client.addJob({ template: { src: 'http://example.com/project.aep', composition: 'main' }, assets: [{ type: 'data', layerName: 'subtitle', property: 'Source Text', value: 'Dynamic Text' }], actions: { postrender: [{ module: '@nexrender/action-encode', preset: 'mp4', output: 'out.mp4' }] }, }); emitter.on('created', job => console.log('job created:', job.uid)); emitter.on('started', job => console.log('job started')); emitter.on('progress', (job, pct) => console.log(`progress: ${pct}%`)); emitter.on('finished', job => console.log('done! output:', job.output)); emitter.on('error', err => console.error('error:', err)); // Other client operations const jobs = await client.listJobs(); const job = await client.fetchJob('V1StGXR8Z5jdHi6B'); await client.updateJob('V1StGXR8Z5jdHi6B', { state: 'queued' }); await client.removeJob('V1StGXR8Z5jdHi6B'); // Re-subscribe to events for an already-created job const emitter2 = await client.resumeJob('V1StGXR8Z5jdHi6B'); emitter2.on('finished', job => console.log('resumed job finished')); ``` --- ## @nexrender/worker — `start(host, secret, settings, headers?)` Starts a continuous render worker loop that polls `nexrender-server` for queued jobs, picks one up (marking it as `picked`), runs the full `@nexrender/core` render pipeline, and updates the server with progress and final state. The worker respects tag-based job selection, scheduled stop times, and graceful interruption. ```js const { start, stop, isRunning } = require('@nexrender/worker'); start( 'http://my.server.com:3050', 'my-super-secret', { binary: '/Applications/Adobe After Effects 2024/aerender', workpath: '/mnt/fast-ssd/renders', addLicense: true, stopOnError: false, // continue after per-job errors tagSelector: 'gpu,hires', // only pick jobs with these tags polling: 15000, // check every 15 s (overrides NEXRENDER_API_POLLING) tolerateEmptyQueues: 5, // exit after 5 consecutive empty polls exitOnEmptyQueue: true, stopAtTime: '23:00', // stop at 11 PM stopDays: '1,2,3,4,5', // weekdays only (0=Sun, 6=Sat) waitBetweenJobs: 2000, // 2 s cooldown between jobs multiFrames: true, handleInterruption: true, // graceful SIGINT/SIGTERM: requeue current job name: 'worker-gpu-01', onFinished: job => console.log(`[${job.uid}] finished in ${job.finishedAt - job.startedAt}ms`), onError: (job, err) => console.error(`[${job.uid}] error:`, err.message), }, { 'x-datacenter': 'us-east-1' } // forwarded as HTTP headers to server ); // Programmatic stop (completes current job first) setTimeout(() => { if (isRunning()) stop(); }, 3600000); // stop after 1 hour ``` ```bash # Binary usage nexrender-worker \ --host=http://my.server.com:3050 \ --secret=my-super-secret \ --name=worker-01 \ --tag-selector=gpu \ --polling=10000 \ --exit-on-empty-queue \ --tolerate-empty-queues=3 \ --stop-at-time=23:00 \ --stop-days=1,2,3,4,5 \ --multi-frames \ --no-analytics ``` --- ## @nexrender/cli — local single-job rendering `nexrender-cli` renders a single job from the command line without requiring a server or worker. It accepts the job JSON either as an inline argument or via a file path. ```bash # Inline JSON nexrender-cli '{"template":{"src":"file:///home/user/project.aep","composition":"main"}}' # From file nexrender-cli --file myjob.json # Advanced flags nexrender-cli --file myjob.json \ --binary "/Applications/Adobe After Effects 2024/aerender" \ --workpath /tmp/renders \ --skip-cleanup \ --debug \ --multi-frames \ --max-memory-percent 70 \ --max-render-timeout 600 \ --no-license \ --no-analytics # WSL (Windows Subsystem for Linux) nexrender-cli --file myjob.json --wsl-map "Z" # Forward raw aerender parameters nexrender-cli --file myjob.json --ae 'close SAVE_CHANGES' --ae 'i 10' # Cleanup temp folder nexrender-cli --cleanup ``` --- ## @nexrender/types — `create(jobConfig)` and `validate(job)` `create()` normalizes a raw job object by filling in defaults (nanoid UID, `state: 'created'`, empty asset/action arrays, etc.). `validate()` asserts all required fields are present and throws on invalid asset/action structure. `getRenderingStatus()` returns a lightweight subset of job fields suitable for server-to-worker status updates. ```js const { create, validate, getRenderingStatus } = require('@nexrender/types/job'); // create() fills in defaults const job = create({ template: { src: 'https://example.com/project.aep', composition: 'main', outputModule: 'Lossless', frameStart: 0, frameEnd: 60, }, assets: [ { src: 'https://example.com/overlay.png', type: 'image', layerName: 'overlay.png', }, ], tags: 'batch', priority: 5, }); console.log(job.uid); // 'V1StGXR8Z5jdHi6B' (nanoid) console.log(job.state); // 'created' console.log(job.type); // 'default' // validate() throws AssertionError if job is malformed try { validate(job); // returns true on success } catch (err) { console.error('Invalid job:', err.message); } // getRenderingStatus() for lightweight server updates const status = getRenderingStatus(job); // { uid, state, type, tags, renderProgress, error, createdAt, updatedAt, startedAt, finishedAt, timings } ``` --- ## Job asset type: `data` The `data` asset type injects dynamic values or AE expressions directly into layer properties without modifying the `.aep` file. It supports deep property paths using dot notation, nested composition targeting with `->`, and side-by-side `value`/`expression` use. ```json { "assets": [ { "type": "data", "layerName": "title-text", "property": "Source Text", "value": "Q3 Revenue Report" }, { "type": "data", "layerName": "background-solid", "property": "Effects.Fill.Color", "value": [0.1, 0.4, 0.9] }, { "type": "data", "layerName": "counter", "property": "Source Text", "expression": "Math.round(time * 100).toString() + '%'" }, { "type": "data", "layerName": "logo", "composition": "FULL_HD->intro->logo comp", "property": "Transform.Scale", "value": [120, 120] }, { "type": "data", "layerIndex": 1, "property": "Source Text.font", "value": "Helvetica-Bold" } ] } ``` --- ## Job asset type: `script` The `script` asset type injects a JSX file into the AE rendering pipeline and optionally passes typed dynamic parameters to it. Parameters are accessed inside the JSX via `NX.get(key)`, `NX.call(key, args)`, and `NX.arg(key)`. The `keyword` field overrides the `NX` namespace. ```json { "assets": [ { "src": "https://example.com/scripts/branding.jsx", "type": "script", "keyword": "CFG", "parameters": [ { "key": "brandColor", "value": [1, 0.5, 0] }, { "key": "showLogo", "value": true }, { "key": "tagline", "value": "Powered by nexrender" }, { "key": "frameCount", "value": null }, { "key": "getTimestamp", "value": "(function() { return new Date().toISOString(); })()" } ] } ] } ``` ```jsx // branding.jsx — runs inside After Effects before render var color = CFG.get("brandColor"); // [1, 0.5, 0] var show = CFG.get("showLogo"); // true var line = CFG.get("tagline"); // "Powered by nexrender" var comp = app.project.activeItem; var layer = comp.layer("brand-bg"); layer.effect("Fill")("Color").setValue(color); if (!show) { comp.layer("logo").enabled = false; } ``` --- ## @nexrender/action-encode — ffmpeg post-render encoding `action-encode` wraps a bundled (or `NEXRENDER_FFMPEG`-pointed) ffmpeg binary to transcode the rendered output. Supported presets: `mp4`, `ogg`, `webm`, `mp3`, `m4a`, `gif`. Custom ffmpeg parameters can be merged over the preset defaults. Use `${workPath}` in parameter values to reference the job working directory. ```json { "actions": { "postrender": [ { "module": "@nexrender/action-encode", "preset": "mp4", "output": "final.mp4" }, { "module": "@nexrender/action-encode", "output": "encoded_playlist_%v.m3u8", "params": { "-acodec": "aac", "-vcodec": "libx264", "-pix_fmt": "yuv420p", "-map": ["0:0", "0:0", "0:0"], "-b:v:0": "2000k", "-b:v:1": "1000k", "-b:v:2": "500k", "-f": "hls", "-hls_time": "10", "-hls_list_size": "0", "-var_stream_map": "v:0,name:high v:1,name:medium v:2,name:low", "-master_pl_name": "master.m3u8", "-hls_segment_filename": "${workPath}\\encoded%d_%v.ts" } }, { "module": "@nexrender/action-encode", "preset": "gif", "output": "preview.gif" } ] } } ``` --- ## @nexrender/action-copy — copy rendered output to a destination `action-copy` copies the render output (or any file in the job workpath) to an absolute or relative destination path. It creates intermediate directories automatically. Setting `useJobId: true` renames the output to the job UID. ```json { "actions": { "postrender": [ { "module": "@nexrender/action-encode", "preset": "mp4", "output": "encoded.mp4" }, { "module": "@nexrender/action-copy", "input": "encoded.mp4", "output": "/mnt/deliveries/2024/q3/" }, { "module": "@nexrender/action-copy", "input": "encoded.mp4", "output": "/archive/renders/", "useJobId": true } ] } } ``` --- ## @nexrender/action-upload — upload to cloud storage `action-upload` delegates file upload to an installed `@nexrender/provider-*` package (or any globally installed npm package). The `provider` field selects the backend; `params` are forwarded to the provider's `upload()` function. ```json { "actions": { "postrender": [ { "module": "@nexrender/action-encode", "preset": "mp4", "output": "result.mp4" }, { "module": "@nexrender/action-upload", "input": "result.mp4", "provider": "s3", "params": { "region": "us-east-1", "bucket": "my-render-bucket", "key": "renders/2024/result.mp4", "contentType": "video/mp4", "acl": "public-read", "cacheControl": "max-age=31536000", "credentials": { "accessKeyId": "AKIA...", "secretAccessKey": "..." } } }, { "module": "@nexrender/action-upload", "provider": "gs", "params": { "bucket": "my-gcs-bucket", "item": "renders/result.mp4", "contentType": "video/mp4", "resumable": true } } ] } } ``` --- ## @nexrender/action-cache — predownload/postdownload asset caching `action-cache` must run in both `predownload` (to serve cached files) and `postdownload` (to save newly downloaded files to cache). It skips `file://` assets and respects a TTL for stale-cache invalidation. ```json { "actions": { "predownload": [ { "module": "@nexrender/action-cache", "cacheDirectory": "~/nexrender-cache", "cacheAssets": true, "ttl": 86400000, "useOriginal": true } ], "postdownload": [ { "module": "@nexrender/action-cache", "cacheDirectory": "~/nexrender-cache", "cacheAssets": true } ] } } ``` --- ## @nexrender/action-decompress — extract zip/7z archives before render `action-decompress` runs in `prerender` and extracts any `.zip`, `.7z`, `.rar`, `.tar`, `.gz`, `.bz2`, or `.xz` archive found among the template or assets. Use `format: "zip"` for the built-in AdmZip handler, or `format: "zip-7z"` for the 7-Zip backed handler that also supports non-zip formats. ```json { "template": { "src": "https://example.com/project-bundle.zip", "composition": "main" }, "assets": [ { "src": "https://example.com/footage.zip", "type": "video", "layerName": "clip.mp4", "decompressed": "clip.mp4" } ], "actions": { "prerender": [ { "module": "@nexrender/action-decompress", "format": "zip" } ] } } ``` --- ## @nexrender/action-compress — zip files after render `action-compress` packages one or more output files (or directories) into a zip archive after rendering. It supports AE image sequences using the `#####` pattern wildcard. ```json { "actions": { "postrender": [ { "module": "@nexrender/action-compress", "format": "zip", "input": ["result.mp4", "preview.gif", "subtitles.srt"], "output": "delivery-package.zip" }, { "module": "@nexrender/action-compress", "format": "zip", "input": ["frame_#####.png"], "output": "image-sequence.zip" } ] } } ``` --- ## @nexrender/action-image — image manipulation with jimp `action-image` applies a sequence of [jimp](https://github.com/jimp-dev/jimp) filter operations to an image in the job workpath. It can also create a new blank image using the `create: [width, height]` option instead of reading an existing file. ```json { "actions": { "prerender": [ { "module": "@nexrender/action-image", "input": "thumbnail.jpg", "output": "thumbnail-processed.jpg", "filters": [ { "name": "resize", "args": [1920, 1080] }, { "name": "quality", "args": [85] }, { "name": "blur", "args": [2] }, { "name": "brightness","args": [0.1] }, { "name": "contrast", "args": [-0.1] } ] }, { "module": "@nexrender/action-image", "create": [500, 500], "output": "blank-overlay.png", "filters": [ { "name": "background", "args": [2164260863] } ] } ] } } ``` --- ## @nexrender/action-fonts — install fonts before rendering `action-fonts` installs `.ttf`/`.otf` font files (supplied as `static` assets) into the OS font directory before rendering and optionally removes them after. Works on macOS and Windows. The `name` field on each font asset is required. ```json { "assets": [ { "src": "https://example.com/fonts/BrandFont-Bold.ttf", "type": "static", "name": "BrandFont-Bold.ttf" }, { "src": "https://example.com/fonts/BrandFont-Regular.ttf", "type": "static", "name": "BrandFont-Regular.ttf" } ], "actions": { "prerender": [ { "module": "@nexrender/action-fonts" } ] } } ``` ```bash # Enable font removal after render (default: disabled for safety) NX_ENABLE_FONT_REMOVAL=true nexrender-worker --host=http://localhost:3050 --secret=mysecret ``` --- ## @nexrender/action-webhook — HTTP callbacks on render events `action-webhook` sends an HTTP request (default: POST) to an arbitrary URL at any action stage. Job fields can be interpolated into the URL, headers, and body using `{job.fieldPath}` syntax. ```json { "actions": { "postrender": [ { "module": "@nexrender/action-webhook", "url": "https://hooks.example.com/render-done/{job.uid}", "method": "POST", "headers": { "Content-Type": "application/json", "X-Job-State": "{job.state}" }, "json": { "jobId": "{job.uid}", "output": "{job.output}", "duration": "{job.timings.dorender}", "tags": "{job.tags}" } } ] } } ``` --- ## @nexrender/action-lottie — render AE composition to Lottie HTML banner `action-lottie` (alpha) converts an AE composition into a self-contained Lottie HTML5 banner by injecting the Bodymovin exporter JSX, spinning up a local asset server, and producing an HTML banner directory. Use with `@nexrender/action-compress` to bundle the output for delivery. ```json { "template": { "src": "https://example.com/banner.aep", "composition": "Banner_300x250" }, "actions": { "predownload": [ { "module": "@nexrender/action-lottie", "banner": { "lottie_origin": "local", "lottie_renderer": "svg", "lottie_library": "full", "use_original_sizes": false, "width": 300, "height": 250, "click_tag": "https://example.com", "shouldLoop": true, "loopCount": 3 } } ], "postrender": [ { "module": "@nexrender/action-compress", "format": "zip", "input": ["banner"], "output": "banner_300x250.zip" } ] } } ``` --- ## @nexrender/provider-s3 — Amazon S3 asset provider `provider-s3` supports both downloading assets from S3 URIs and uploading rendered files to S3 buckets. Credentials are resolved from inline params, AWS profile, or environment variables (`AWS_ACCESS_KEY` / `AWS_SECRET_KEY`). Supports custom endpoints (e.g., Cloudflare R2, MinIO). ```json { "template": { "src": "s3://my-bucket/projects/project.aep", "composition": "main" }, "assets": [ { "src": "s3://my-bucket/assets/logo.png", "type": "image", "layerName": "logo.png", "params": { "credentials": { "accessKeyId": "AKIA...", "secretAccessKey": "..." }, "region": "us-east-1" } } ], "actions": { "postrender": [ { "module": "@nexrender/action-upload", "provider": "s3", "params": { "endpoint": "https://<account>.r2.cloudflarestorage.com", "bucket": "renders", "key": "output/final.mp4", "credentials": { "accessKeyId": "...", "secretAccessKey": "..." } } } ] } } ``` --- ## @nexrender/database-redis — Redis job store Replaces the default file-based job store with a Redis backend. Set `NEXRENDER_DATABASE_PROVIDER=redis` and `REDIS_URL` before starting the server. All jobs are stored under `nexjob:<uid>` keys. ```bash NEXRENDER_DATABASE_PROVIDER=redis \ REDIS_URL=redis://localhost:6379 \ nexrender-server --port=3050 --secret=mysecret ``` ```js // Programmatic server with Redis process.env.NEXRENDER_DATABASE_PROVIDER = 'redis'; process.env.REDIS_URL = 'redis://:password@redis.example.com:6379/0'; const { listen } = require('@nexrender/server'); listen(3050, 'mysecret', () => console.log('Server running with Redis backend')); ``` --- ## Job ordering and queue control (server env vars) The server supports multiple queue ordering strategies and parallelism controls configurable via environment variables, without code changes. ```bash # FIFO (default): oldest-first nexrender-server --port=3050 # Newest-first NEXRENDER_ORDERING=newest-first nexrender-server --port=3050 # Priority-based (uses job.priority field, higher = first) NEXRENDER_ORDERING=priority nexrender-server --port=3050 # Random NEXRENDER_ORDERING=random nexrender-server --port=3050 # Custom database file location NEXRENDER_DATABASE=/data/nexrender.json nexrender-server --port=3050 # Worker: tune download timeout (default 3 min) NEXRENDER_DOWNLOAD_TIMEOUT=600000 nexrender-worker --host=http://localhost:3050 # Worker: graceful shutdown trigger via lock file touch /path/to/worker/.nexrender-worker.lock # The worker will finish the current job and exit cleanly ``` --- nexrender is best suited for two primary use cases: **local batch rendering** using `nexrender-cli` and `@nexrender/core` directly from Node.js scripts, and **distributed render farm operation** using the server/worker/API client triad. In the local case, a developer imports `render()` from `@nexrender/core`, constructs a job object programmatically, and chains together actions like `action-encode` and `action-copy` to produce deliverable files. This workflow integrates naturally into CI/CD pipelines, serverless functions, or backend services that need to generate personalized video on demand. For farm deployments, `nexrender-server` acts as a job broker backed by a file or Redis store, and any number of `nexrender-worker` processes (on the same machine or across a network) poll for jobs filtered by tag, process them with the full action pipeline, and push progress back to the server. The `@nexrender/api` client library and the REST API allow external applications — dashboards, e-commerce platforms, media production tools — to submit jobs, track their lifecycle via events, and retrieve results. Cloud provider packages (`provider-s3`, `provider-gs`, `provider-ftp`, `provider-sftp`) enable fully cloud-native pipelines where both source assets and rendered output live in object storage, requiring no shared filesystem between workers.