> ## Documentation Index
> Fetch the complete documentation index at: https://docs.springwinter.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Deploy a Node.js Background Worker on Springwinter

> Run Node.js queue consumers and background jobs on AWS ECS with Springwinter using reliable shutdown, idempotency, concurrency limits, retries, and CloudWatch logs.

Springwinter workers run long-lived container processes without a public URL. They fit Node.js queue consumers, event processors, schedulers, and asynchronous jobs that continuously pull work.

*Updated October 9, 2026.*

**A reliable worker acknowledges a job only after success and stops fetching new work during shutdown.**

## Design the processing loop

The queue is the source of truth. Keep concurrency bounded, make handlers idempotent, and distinguish retryable failures from permanent failures.

```js theme={null}
let stopping = false;

async function run() {
  while (!stopping) {
    const messages = await queue.receive({ waitSeconds: 20, max: 5 });
    await Promise.all(messages.map(async (message) => {
      await processIdempotently(message);
      await queue.acknowledge(message);
    }));
  }
}

process.on("SIGTERM", () => { stopping = true; });
process.on("SIGINT", () => { stopping = true; });

await run();
```

Real code should set a maximum drain period. If processing can exceed that period, extend queue visibility while the job runs and allow retries after interruption.

## Package the worker

Use a multi-stage Dockerfile and run Node directly. The worker does not need `EXPOSE` or an HTTP server.

```dockerfile theme={null}
FROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:24-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app ./
USER node
CMD ["node", "dist/worker.js"]
```

## Deploy on Springwinter

<Steps>
  <Step title="Create a Worker resource">
    In your project, click **Add resource** and select **Worker**.
  </Step>

  <Step title="Select the code">
    Choose the GitHub repository and production branch. Set the start command only when the image does not already define one.
  </Step>

  <Step title="Set compute and configuration">
    Choose CPU and memory, then add queue URLs, concurrency, timeouts, and service credentials as environment variables.
  </Step>

  <Step title="Verify processing">
    Deploy a test message. Confirm one successful side effect, one acknowledgement, and useful structured logs.
  </Step>
</Steps>

## Reliability rules

* Use a durable queue rather than in-memory job state.
* Include an idempotency key in each message.
* Acknowledge only after the durable side effect succeeds.
* Configure dead-letter handling for repeatedly failing jobs.
* Keep concurrency below downstream database and API limits.
* Emit job duration, success, retry, and failure counters.
* Test container replacement while jobs are running.

## Scaling and cost

Increase task size when each job needs more CPU or memory. Increase worker replicas when jobs are independent and queue depth rises. Current Springwinter workers are continuously running services, so they are not a scale-to-zero substitute for one-off ECS tasks or AWS Batch.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Should a Node.js worker expose a health endpoint?">
    A Springwinter Worker does not expose a public HTTP port. Use process liveness, queue metrics, job completion metrics, and logs to determine whether it is making progress.
  </Accordion>

  <Accordion title="Can one worker process several jobs concurrently?">
    Yes, but bound concurrency explicitly. Unlimited promises can exhaust memory, database connections, queue visibility windows, or third-party rate limits.
  </Accordion>

  <Accordion title="How should deployments handle in-flight jobs?">
    Stop polling after `SIGTERM`, finish work within the drain window, then exit. Jobs that cannot finish safely must become visible again and be idempotent when retried.
  </Accordion>
</AccordionGroup>

## Sources and further reading

* [Springwinter workers](/deploy/workers)
* [Node.js Docker best practices](https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md)
* [Amazon SQS visibility timeouts](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.