Antrykntryk
Antryk
ProductsDocsPricingBlogsAboutContact

Antryk

Antryk

Deploy in seconds, scale infinitely, and manage effortlessly — your next-gen cloud solution for modern applications.

Explore

  • Home
  • Products
  • Pricing
  • Blogs
  • Npm Packages
  • Terms of Use
  • Privacy Policy
  • Payment & Refund Policy
  • About Us
  • Contact Us
  • Reviews

Connect

© 2026 Antryk. All rights reserved.
Back to Blogs
AI Agents Deployment10 min read

How to Deploy an AI Agent: Background Worker vs Web Service (With Working Code)

Long-running AI agents time out behind HTTP requests. Split yours into a web service and a queue-fed background worker, deployed from GitHub in a few clicks.

MuthuSelvi
October 19, 2026
How to Deploy an AI Agent: Background Worker vs Web Service (With Working Code)

How to Deploy an AI Agent: Background Worker vs Web Service (With Working Code)#

An agent that answers in four seconds on a laptop behaves very differently once real users arrive. Picture a support agent that reads a ticket, searches a knowledge base, calls two internal APIs, and drafts a reply. That chain can easily run for ninety seconds. Somewhere between the browser and your code, a gateway gives up at thirty or sixty, the user sees an error, and the agent keeps going anyway, burning tokens for nobody.

Most "how to deploy an AI agent" advice stops at a Dockerfile and a prayer. This guide goes after the part that actually breaks in production: choosing between a web service and a background worker, wiring a queue between them, and putting the whole thing live from a GitHub repo in a handful of clicks on Antryk.

Two ways to run an agent in production#

Every deployed agent ends up as one of two shapes, and sometimes both at once.

A web service listens for HTTP requests and answers them. It gets a public URL, which makes it the natural home for chat endpoints, webhook receivers, and anything a browser or another system needs to call directly. On Antryk, a web service can have a free domain attached, so a working endpoint takes no DNS work.

A background worker has no public address at all. It runs continuously, pulls jobs from a queue, a database, or a scheduler, and works through them at its own pace. Antryk's worker service supports Redis queues, RabbitMQ, PostgreSQL queues, Amazon SQS, and Kafka consumers, plus custom queue code.

Web serviceBackground worker
Public URLYes (free domain available)No
Run longer than a minuteFragileBuilt for it
Triggered byAn HTTP requestA queue message, a schedule, a handoff
Retries after a failureYou write themNatural fit with queue retries
Good forChat endpoints, streaming, webhooksResearch agents, report builders, data pipelines, monitoring agents

Most production agents use both. A thin web service accepts the request and drops a job onto a queue; the worker does the thinking; the web service hands back the result when it's ready.

Why a long-running agent breaks behind an HTTP request#

Request timeouts are the obvious problem, but the nastier ones hide behind them.

When a proxy cuts the connection at its limit, the client receives a 504 while your process carries on. Nobody reads the output, yet every LLM call is still billed. Worse, a client that retries after the error starts a second copy of the same run. If the agent sends emails, creates tickets, or charges cards, those side effects now happen twice.

Moving the work off the request path fixes all three. The caller gets an immediate acknowledgement with a job ID, the worker owns the run from start to finish, and retries become a deliberate queue feature instead of an accident.

The pattern: accept, queue, work, report#

Four moving parts, none of them exotic:

  1. 1

    The API receives a task and writes a job to the queue with a unique ID.

  2. 2

    The API answers right away with 202 Accepted and that ID.

  3. 3

    The worker pulls the job, runs the agent, and stores the result under the same ID.

  4. 4

    The client polls (or you push a notification) until the status flips to done.

Here is a minimal worker. It uses a plain Redis list so the logic stays readable; swap in your own agent call where the placeholder sits.

python
# worker.py
import json
import os
import signal
import time

import redis

r = redis.Redis.from_url(os.environ["REDIS_URL"], decode_responses=True)

QUEUE = "agent:jobs"
DEAD = "agent:dead"
MAX_ATTEMPTS = 3
running = True


def stop(signum, frame):
    global running
    running = False


signal.signal(signal.SIGTERM, stop)
signal.signal(signal.SIGINT, stop)


def run_agent(task: str) -> str:
    # Call your LangGraph graph, CrewAI crew, or plain LLM loop here.
    # Enforce your own step limit and timeout inside this function.
    return f"processed: {task}"


def save(job_id: str, payload: dict) -> None:
    r.set(f"agent:result:{job_id}", json.dumps(payload), ex=86400)


while running:
    # TTL must be longer than your slowest agent run, or the heartbeat
    # will expire mid-job and trigger a false alarm.
    r.set("agent:heartbeat", int(time.time()), ex=900)

    item = r.blpop(QUEUE, timeout=5)
    if item is None:
        continue

    job = json.loads(item[1])
    try:
        save(job["id"], {"status": "running"})
        output = run_agent(job["input"])
        save(job["id"], {"status": "done", "output": output})
    except Exception as exc:
        job["attempts"] = job.get("attempts", 0) + 1
        if job["attempts"] < MAX_ATTEMPTS:
            r.rpush(QUEUE, json.dumps(job))
        else:
            r.rpush(DEAD, json.dumps({**job, "error": str(exc)}))
            save(job["id"], {"status": "failed", "error": str(exc)})

And the API that feeds it:

python
# api.py
import json
import os
import uuid

import redis
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()
r = redis.Redis.from_url(os.environ["REDIS_URL"], decode_responses=True)


class RunRequest(BaseModel):
    input: str


@app.post("/runs", status_code=202)
def create_run(body: RunRequest):
    job_id = str(uuid.uuid4())
    r.rpush("agent:jobs", json.dumps({"id": job_id, "input": body.input}))
    return {"id": job_id, "status": "queued"}


@app.get("/runs/{job_id}")
def get_run(job_id: str):
    raw = r.get(f"agent:result:{job_id}")
    if raw is None:
        return {"id": job_id, "status": "queued"}
    return json.loads(raw)


@app.get("/health")
def health():
    if r.get("agent:heartbeat") is None:
        raise HTTPException(status_code=503, detail="worker heartbeat missing")
    return {"status": "ok"}

Your requirements.txt needs three lines: redis, fastapi, and uvicorn[standard].

One honest caveat about the worker above. A job popped from a Redis list disappears from the queue the instant it is read, so a worker that crashes mid-run loses that job. For low-stakes work that is acceptable. For anything where a lost job costs money or trust, use a queue that supports acknowledgements (RabbitMQ, SQS, or Redis Streams) and make each run idempotent, meaning a second delivery of the same job ID produces no second side effect.

Deploying the worker on Antryk, step by step#

With the repo pushed to GitHub, GitLab, or Bitbucket, the worker goes live from the dashboard. No Dockerfile appears anywhere in the flow.

  1. 1

    Open Background Workers in the sidebar and choose Deploy Worker.

  2. 2

    Name the service and pick your Git provider, repository, and branch.

  3. 3

    Fill in the build settings. For the Python worker above: install command pip install -r requirements.txt, start command python worker.py, runtime version 3.11. The framework is detected automatically.

  4. 4

    Choose a region (USA, Europe, or Asia).

  5. 5

    Add environment variables: REDIS_URL plus your model provider's API key. A .env file can be imported in one go, and secrets stay out of the repository.

  6. 6

    Click Deploy.

From then on, every push to the connected branch triggers a fresh deployment, and a failed deployment never replaces the one that's currently running. Live logs stream in the dashboard with filters for the last hour, six hours, or 24 hours, which is where you'll watch jobs move through retries. Failed workers restart automatically.

For traffic spikes, the scaling settings take a minimum and maximum instance count, queue thresholds, concurrency limits, and scale-down delays. Several workers can consume the same Redis list at once, so the code above scales out without changes. Full details live in the Background Workers documentation.

Adding the web service when you need a public URL#

If the agent is triggered by a schedule or by other backend code, the worker alone is enough. When a browser, a mobile app, or a third-party webhook needs to reach it, deploy api.py as a web service from the same repository (or a second one), point it at the same REDIS_URL, and use the free domain Antryk attaches to get a working HTTPS endpoint. Start commands and ports follow your framework's usual setup.

Keep that API boring. Its entire job is validating input, enqueueing, and reporting status; the thinking stays in the worker.

Which worker plan fits which agent#

Agents that call hosted model APIs spend most of their time waiting on the network, so memory tends to matter more than CPU. These are the worker plans listed on the pricing page at the time of writing:

PlanRAMCPUPriceReasonable fit
Starter512 MB0.5$4.99/moPrototypes, simple single-tool agents
Starter Plus1 GB1$7.99/moLight agents with a few integrations
Standard2 GB1$15.99/moFirst production agent for a small app
Pro4 GB2$31.99/moSeveral concurrent runs, heavier frameworks
Pro Plus8 GB2$61.99/moIn-memory document processing, larger contexts
Pro Max16 GB4$120.99/moHigh-compute AI workloads, browser automation

Treat the "reasonable fit" column as a starting point rather than a rule. Begin one tier lower than you expect to need, watch the logs for out-of-memory restarts, and move up when they appear. Running a model locally on the worker rather than calling an API is a different story entirely, since that wants GPU hardware instead of a CPU plan.

One more number deserves daylight: for chatty agents, the model provider's bill usually dwarfs the hosting bill. Log token usage per job from day one.

Giving the agent memory#

Agents that answer from your own documents need somewhere to keep embeddings. Running the vector store next to the worker keeps retrieval latency low and the architecture in one place. Antryk offers managed Qdrant, Milvus, Weaviate, and Chroma deployments, so the worker can reach its retrieval layer without a separate vendor account.

Production checklist for a deployed AI agent#

  • [ ] Every run has a hard step limit and a wall-clock timeout inside run_agent

  • [ ] Jobs carry a unique ID, and side effects are idempotent

  • [ ] Retries are capped, with a dead-letter list for jobs that never succeed

  • [ ] API keys and credentials live in environment variables, never in the repo

  • [ ] The worker handles SIGTERM so a redeploy doesn't cut a run in half

  • [ ] A heartbeat key proves the worker is alive, and /health reads it

  • [ ] An uptime monitor watches /health and alerts by email, SMS, or webhook

  • [ ] Token usage and cost are logged per job

  • [ ] Logs are structured JSON so failed runs can be searched quickly

The heartbeat item earns its place. A common failure looks like this: the web service answers every health check with a cheerful 200 while the worker behind it has quietly stopped consuming jobs. Users submit tasks, nothing completes, and no alert fires. Tying /health to the worker's heartbeat, as in the code above, turns that silent stall into a loud one. Antryk's free uptime monitors can watch that endpoint over HTTP and notify you the moment it flips to a 503.

Frequently asked questions#

Can an AI agent run as a background worker? Yes, and for anything slower than a typical request it's the better home. The worker pulls tasks from a queue, runs the agent, and stores the result, with no HTTP timeout hanging over the run.

Do I need Docker or Kubernetes to deploy an AI agent? Not on a platform that builds from your repository. Point it at the repo, set install and start commands, add environment variables, and deploy.

Can one agent be deployed as a web service instead? Certainly, and that works well for agents that answer within seconds or stream tokens to a chat window. Once runs stretch past a minute, move the heavy lifting to a worker behind a queue.

Which frameworks work? Anything that runs as a normal Python, Node.js, Go, Ruby, or Java process: LangGraph, CrewAI, the OpenAI Agents SDK, or a hand-rolled loop around a model API. The platform runs your start command; it doesn't care which library sits inside.

The short version#

Agents fail in production for architectural reasons far more often than model reasons. Keep the request path thin, put long work behind a queue, cap retries and runtime, and watch the worker, not just the web endpoint.

To put your own agent live, the Background Workers docs walk through the dashboard flow, and the worker pricing page shows which plan to start on. Push the repo, set the variables, and click deploy.

#deploy ai agent#ai agent deployment#ai agent hosting#background worker#langgraph deployment#crewai deployment#long running ai agents#queue workers#deploy python agent#ai agent production
Share:

MuthuSelvi

SEO Executive

Muthuselvi R is an SEO Executive specializing in SEO, content optimization, keyword research, technical SEO, and organic growth strategies. She enjoys creating search-focused content that helps businesses improve visibility, reach the right audience, and grow online.

On This Page

  • How to Deploy an AI Agent: Background Worker vs Web Service (With Working Code)
  • Two ways to run an agent in production
  • Why a long-running agent breaks behind an HTTP request
  • The pattern: accept, queue, work, report
  • Deploying the worker on Antryk, step by step
  • Adding the web service when you need a public URL
  • Which worker plan fits which agent
  • Giving the agent memory
  • Production checklist for a deployed AI agent
  • Frequently asked questions
  • The short version

Previous Post

Best Static Website Hosting Platforms for Developers in 2026