Category: Без рубрики

  • PostgreSQL Docker Compose: Full Setup Guide 2026

    PostgreSQL Docker Compose: A Complete Setup Guide for Developers

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    Running PostgreSQL locally used to mean installing a system package, fighting with pg_hba.conf, and hoping you didn’t break some other project’s database version. Docker Compose fixes that. With a single YAML file, you get a reproducible, disposable PostgreSQL instance that behaves the same on your laptop, your CI runner, and your production VPS.

    This guide covers everything from a bare-minimum single-container setup to a production-ready configuration with persistent volumes, health checks, backups, and multi-service networking. If you’re deploying alongside other services — like the streaming stack we cover in our Docker networking guide — the same patterns apply.

    Why Use Docker Compose for PostgreSQL

    PostgreSQL is a stateful service, which makes it a slightly trickier candidate for containerization than a stateless API. But Docker Compose handles the hard parts well:

  • Version pinning — lock to postgres:16 and never worry about a host upgrade breaking your data format.
  • Isolation — run PostgreSQL 13 for one project and PostgreSQL 16 for another, side by side, with zero conflicts.
  • Reproducibility — a teammate can clone your repo and run docker compose up to get an identical database.
  • Easy teardowndocker compose down -v wipes the environment cleanly when you’re done testing.
  • The official PostgreSQL Docker image documentation is the canonical reference for supported environment variables and image variants, and it’s worth bookmarking alongside this guide.

    Prerequisites

    Before you start, make sure you have:

  • Docker Engine 20.10+ and Docker Compose v2 (docker compose version should work without a hyphen)
  • A Linux, macOS, or WSL2 environment
  • Basic familiarity with YAML syntax
  • At least 1GB of free disk space for the database volume
  • If you’re setting this up on a fresh VPS, our Docker installation guide for Ubuntu walks through getting Docker Engine installed correctly before you touch Compose.

    Basic PostgreSQL Docker Compose Setup

    Start with the simplest possible configuration. Create a directory for your project and add a docker-compose.yml file:

    version: "3.9"
    
    services:
      db:
        image: postgres:16
        container_name: pg_container
        restart: unless-stopped
        environment:
          POSTGRES_USER: appuser
          POSTGRES_PASSWORD: changeme
          POSTGRES_DB: appdb
        ports:
          - "5432:5432"
        volumes:
          - pgdata:/var/lib/postgresql/data
    
    volumes:
      pgdata:

    Bring it up with:

    docker compose up -d

    Check that it’s running:

    docker compose ps
    docker compose logs db

    Connect to it using psql from your host machine (assuming the client is installed):

    psql -h localhost -U appuser -d appdb

    Or connect from inside the container without installing anything locally:

    docker compose exec db psql -U appuser -d appdb

    That’s a working database. But this bare-bones version has real problems for anything beyond a quick local test: the password is hardcoded in plaintext, there’s no health check, and you have no backup strategy. Let’s fix each of those.

    Environment Variables and Secrets

    Hardcoding credentials into docker-compose.yml is a bad habit that tends to leak into git history. Use a .env file instead:

    # .env
    POSTGRES_USER=appuser
    POSTGRES_PASSWORD=a-much-stronger-password-here
    POSTGRES_DB=appdb

    Update the compose file to reference these variables:

    services:
      db:
        image: postgres:16
        restart: unless-stopped
        env_file:
          - .env
        volumes:
          - pgdata:/var/lib/postgresql/data
    
    volumes:
      pgdata:

    Add .env to your .gitignore immediately. For production deployments, consider Docker secrets or a dedicated secrets manager rather than environment files — plaintext env vars are visible to anyone who can run docker inspect on the container.

    If you need stronger secrets hygiene on a VPS, a managed platform like DigitalOcean offers integrated secrets and managed database options that reduce your operational burden compared to self-hosting everything by hand.

    Persistent Volumes and Data Safety

    The pgdata named volume in the examples above is what keeps your data alive across container restarts and rebuilds. Without it, running docker compose down or recreating the container wipes your database completely.

    A few important volume behaviors to understand:

  • docker compose down (without -v) preserves named volumes — your data survives.
  • docker compose down -v deletes volumes — your data is gone. Use this only when you actually want a clean slate.
  • Bind mounts (./data:/var/lib/postgresql/data) give you direct filesystem access to the data files, which is useful for backups but can cause permission issues between host and container UIDs.
  • Here’s a bind-mount variant if you want the database files visible on your host filesystem:

    services:
      db:
        image: postgres:16
        restart: unless-stopped
        env_file:
          - .env
        volumes:
          - ./pgdata:/var/lib/postgresql/data

    Most teams prefer named volumes for portability and let Docker manage the storage location, reserving bind mounts for cases where you need direct host access, like custom backup scripts.

    Health Checks and Restart Policies

    A container that’s “running” isn’t necessarily ready to accept connections. PostgreSQL takes a moment to initialize, especially on first boot when it’s creating the database cluster. Add a health check so dependent services wait properly:

    services:
      db:
        image: postgres:16
        restart: unless-stopped
        env_file:
          - .env
        volumes:
          - pgdata:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
          interval: 10s
          timeout: 5s
          retries: 5
    
      app:
        build: .
        depends_on:
          db:
            condition: service_healthy
        environment:
          DATABASE_URL: postgresql://appuser:changeme@db:5432/appdb
    
    volumes:
      pgdata:

    The depends_on with condition: service_healthy ensures your application container won’t start trying to connect until PostgreSQL actually reports ready via pg_isready. This eliminates the classic “connection refused” error on cold starts that plagues naive Compose setups.

    Networking Between Services

    By default, Compose creates a private network for all services defined in the same file. The app service above reaches PostgreSQL simply by using db as the hostname — Docker’s internal DNS resolves it automatically. You don’t need to expose port 5432 to the host at all unless you want external tools (like a local GUI client) to connect directly.

    If you do need host access for debugging with a tool like pgAdmin or DBeaver, keep the port mapping but restrict it:

    ports:
      - "127.0.0.1:5432:5432"

    Binding to 127.0.0.1 instead of 0.0.0.0 prevents the database port from being exposed on your machine’s public network interface — a small change that matters a lot if you’re running this on a cloud VPS rather than a laptop.

    Multi-Container Example with pgAdmin

    For local development, it’s often convenient to run a web-based admin UI alongside PostgreSQL:

    services:
      db:
        image: postgres:16
        restart: unless-stopped
        env_file:
          - .env
        volumes:
          - pgdata:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
          interval: 10s
          timeout: 5s
          retries: 5
    
      pgadmin:
        image: dpage/pgadmin4:latest
        restart: unless-stopped
        environment:
          PGADMIN_DEFAULT_EMAIL: [email protected]
          PGADMIN_DEFAULT_PASSWORD: changeme
        ports:
          - "127.0.0.1:8080:80"
        depends_on:
          - db
    
    volumes:
      pgdata:

    Access pgAdmin at http://localhost:8080, then add a new server connection pointing to host db, port 5432, using the credentials from your .env file.

    Backups and Restores

    A container-friendly database is only useful if you can actually back it up. pg_dump works the same way it does on bare metal, just executed inside the container:

    docker compose exec db pg_dump -U appuser appdb > backup.sql

    Restoring is the inverse:

    cat backup.sql | docker compose exec -T db psql -U appuser -d appdb

    For scheduled backups, wire this into a cron job on the host:

    # crontab entry - daily backup at 2am
    0 2 * * * cd /opt/myapp && docker compose exec -T db pg_dump -U appuser appdb | gzip > /backups/appdb-$(date +%F).sql.gz

    For anything running in production, don’t rely on cron alone — pair it with monitoring that alerts you if a backup job silently fails. A service like BetterStack can monitor your backup script’s heartbeat and page you if a scheduled dump doesn’t complete, which is far better than discovering a broken backup chain during an actual outage.

    Upgrading PostgreSQL Versions Safely

    PostgreSQL’s on-disk format isn’t guaranteed compatible across major versions, so you can’t simply bump postgres:15 to postgres:16 in your compose file and expect the existing volume to work. The safe path is:

    1. Take a full pg_dump of your current database.
    2. Stop the old container: docker compose down (keep the volume for now, don’t use -v).
    3. Rename or remove the old volume, then update the image tag in docker-compose.yml.
    4. Start the new version: docker compose up -d.
    5. Restore the dump into the fresh instance.

    Skipping the dump-and-restore step is the single most common cause of “my data disappeared after upgrading” reports in Docker forums. Treat every major version bump as a migration, not a config change.

    Production Hardening Checklist

    Before you point real traffic at a containerized PostgreSQL instance, review these items:

  • Set strong, unique passwords and rotate them periodically.
  • Bind the host port to 127.0.0.1 or remove the port mapping entirely if only internal services need access.
  • Enable regular automated backups with off-host storage (S3, Backblaze, or similar).
  • Use restart: unless-stopped so the database recovers automatically after a host reboot.
  • Monitor disk usage on the volume — PostgreSQL doesn’t cap growth automatically.
  • Pin the image tag (postgres:16.3, not just postgres:latest) to avoid surprise upgrades.
  • Consider running PostgreSQL on a dedicated, appropriately sized VPS rather than co-locating it with CPU-heavy application containers.
  • If you’re hosting this yourself rather than using a managed database, providers like Hetzner offer solid price-to-performance ratios for dedicated database VPS instances, which matters since PostgreSQL performance is heavily tied to disk I/O and available RAM for caching.

    Resource Limits

    Compose lets you cap CPU and memory so a runaway query doesn’t starve other containers on the same host:

    services:
      db:
        image: postgres:16
        deploy:
          resources:
            limits:
              cpus: "2"
              memory: 2G

    Note that deploy.resources is fully honored under Docker Swarm; under plain docker compose up, Compose v2 does apply these limits on recent Docker Engine versions, but it’s worth verifying with docker stats after deployment rather than assuming it’s enforced.

    Common Errors and Fixes

    A few issues come up repeatedly when people first containerize PostgreSQL:

  • “role does not exist” — usually means the POSTGRES_USER env var wasn’t set before the volume was initialized for the first time. Environment variables only take effect on first container creation; changing them later has no effect on an existing volume.
  • “connection refused” — the health check isn’t wired up, and your app is connecting before PostgreSQL finishes booting.
  • Permission denied on data directory — usually a bind-mount UID mismatch between host and container. Named volumes avoid this entirely.
  • Data vanished after docker compose down — someone ran docker compose down -v and deleted the volume along with the containers.
  • Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Do I need to install PostgreSQL on my host machine if I’m using Docker Compose?
    No. The postgres image includes a full PostgreSQL server. You only need the psql client locally if you want to connect from outside the container — otherwise docker compose exec db psql gives you a shell inside the container itself.

    How do I change the PostgreSQL password after the container has already been created?
    Environment variables like POSTGRES_PASSWORD only apply during the initial database cluster creation. To change it afterward, connect with psql and run ALTER USER appuser WITH PASSWORD 'newpassword';, then update your .env file to match for future reference.

    Can I run multiple PostgreSQL versions on the same machine with Docker Compose?
    Yes. Since each project’s containers and volumes are isolated by Compose project name, you can run postgres:14 for one project and postgres:16 for another on the same host without conflicts, as long as they don’t share the same host port.

    Is it safe to run PostgreSQL in Docker for production workloads?
    Many companies do it successfully, but it requires the same operational rigor as bare-metal PostgreSQL: proper backups, monitoring, resource limits, and a tested upgrade path. If you’d rather not manage that yourself, a managed database service removes most of the operational risk.

    Why does my data disappear every time I rebuild the container?
    You’re likely not using a persistent volume, or you’re running docker compose down -v, which explicitly deletes volumes. Double-check your docker-compose.yml includes a volumes: section mapped to /var/lib/postgresql/data.

    How do I check logs if PostgreSQL fails to start?
    Run docker compose logs db to see the container’s stdout/stderr, which usually includes the specific PostgreSQL startup error, such as a permission issue or a corrupted data directory.

    Wrapping Up

    Docker Compose turns PostgreSQL from a fiddly local install into a reproducible, version-pinned service you can spin up and tear down at will. Start with the basic setup, add health checks and named volumes early, and treat backups as non-negotiable before you trust it with real data. Once you’re comfortable with this pattern, it extends naturally to more complex stacks — pairing PostgreSQL with Redis, application containers, and reverse proxies using the same Compose networking model covered in our Docker networking guide.

    For teams scaling beyond a single VPS, it’s worth evaluating whether self-managed PostgreSQL in Docker still makes sense compared to a managed offering — the trade-off is control versus operational overhead, and that calculus changes as your data and uptime requirements grow.

  • AI Agents for Business: Self-Hosted Deployment Guide

    AI Agents for Business: A Practical DevOps Deployment Guide

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    Every vendor pitch deck in 2026 has a slide about AI agents for business — autonomous workflows that triage tickets, write code, manage infrastructure, or handle customer support without a human in the loop. What most of those decks skip is the part engineering teams actually have to deal with: how do you run these agents reliably, keep them secure, and avoid a surprise five-figure API bill? This guide covers the deployment side of AI agents for business, from container architecture to monitoring, written for developers and sysadmins who have to ship and operate this stuff, not just demo it.

    What “AI Agents for Business” Actually Means in Production

    An AI agent, in the production sense, is a process that takes a goal, calls a language model to decide on next steps, executes tools (API calls, shell commands, database queries), and loops until the goal is met or it hits a limit. That’s a very different system from a chatbot. Agents have state, they take actions with side effects, and they need the same operational discipline you’d apply to any other service that touches production data.

    Autonomous vs. Assisted Agents

    Before you write a line of deployment code, decide which pattern you’re building:

  • Assisted agents propose an action and wait for human approval — good for anything touching money, customer data, or infrastructure changes.
  • Autonomous agents execute without a human in the loop — appropriate for low-risk, reversible tasks like log summarization or internal documentation updates.
  • Hybrid agents run autonomously within a scoped permission set and escalate anything outside it.
  • Most businesses starting out should default to assisted or tightly-scoped hybrid agents. The failure mode of a fully autonomous agent with broad tool access is not “it makes a typo” — it’s “it deletes a production table” or “it emails a customer something you can’t unsend.”

    Where Self-Hosting Beats SaaS Agent Platforms

    Hosted agent platforms are fine for prototyping, but three things push serious teams toward self-hosting:

  • Data residency and compliance — you can’t send customer PII through a third-party agent platform if your contracts or regulations forbid it.
  • Cost at scale — per-seat or per-call SaaS pricing gets expensive fast once agents are running continuously rather than on-demand.
  • Control over the model and tool layer — self-hosting lets you swap models, pin versions, and audit exactly what tools an agent can call.
  • If none of those apply to you yet, a managed platform might be the faster path. If any of them do, keep reading.

    Core Architecture for Self-Hosted AI Agents

    A production agent stack has four layers: the model backend, the orchestration/agent framework, the tool/API layer, and the infrastructure it all runs on. Treating these as separate, independently deployable containers is what makes the system maintainable.

    The Container Stack

    A minimal but realistic stack uses Docker Compose to wire together a local LLM backend (or a proxy to a hosted model), an agent orchestration service, and a vector store for retrieval-augmented context. Here’s a working starting point:

    # docker-compose.yml
    version: "3.9"
    services:
      ollama:
        image: ollama/ollama:latest
        volumes:
          - ollama_data:/root/.ollama
        ports:
          - "11434:11434"
        deploy:
          resources:
            reservations:
              devices:
                - driver: nvidia
                  count: 1
                  capabilities: [gpu]
    
      vectordb:
        image: qdrant/qdrant:latest
        volumes:
          - qdrant_data:/qdrant/storage
        ports:
          - "6333:6333"
    
      agent:
        build: ./agent
        depends_on:
          - ollama
          - vectordb
        environment:
          - MODEL_HOST=http://ollama:11434
          - VECTOR_HOST=http://vectordb:6333
          - LOG_LEVEL=info
        restart: unless-stopped
    
      caddy:
        image: caddy:2-alpine
        ports:
          - "443:443"
          - "80:80"
        volumes:
          - ./Caddyfile:/etc/caddy/Caddyfile
          - caddy_data:/data
    
    volumes:
      ollama_data:
      qdrant_data:
      caddy_data:

    This follows the same pattern we cover in our Docker Compose fundamentals guide — each concern gets its own container, and the agent service itself is stateless so you can scale it horizontally behind the reverse proxy. For the full Compose networking and volume options, the official Docker Compose documentation is worth bookmarking.

    Deploying Your First Agent Stack

    With the Compose file in place, bring the stack up and pull a model into the local backend:

    # Bring up the stack
    docker compose up -d
    
    # Pull a model into the local LLM backend
    docker exec -it $(docker compose ps -q ollama) ollama pull llama3.1:8b
    
    # Verify the agent service is healthy
    curl -s http://localhost:8080/health | jq
    
    # Tail logs while you run a test task
    docker compose logs -f agent

    For teams that don’t want to manage GPU infrastructure, Ollama also supports running against smaller quantized models on CPU, which is often good enough for business-process agents that don’t need frontier-model reasoning — think ticket classification or data extraction rather than open-ended coding.

    The agent service itself should expose a small, explicit set of tools rather than unrestricted shell or network access. A typical tool definition looks like this in Python:

    from agent_sdk import tool
    
    @tool(name="lookup_order", description="Fetch order details by ID")
    def lookup_order(order_id: str) -> dict:
        # Read-only, scoped to a single service account
        return orders_db.query("SELECT * FROM orders WHERE id = %s", [order_id])
    
    @tool(name="refund_order", description="Issue a refund, requires human approval")
    def refund_order(order_id: str, amount: float, approved_by: str) -> dict:
        if not approved_by:
            raise PermissionError("Refunds require human approval")
        return payments_api.refund(order_id, amount)

    Notice the second tool enforces an approval gate at the function level, not just in a prompt. Prompts can be manipulated; code paths can’t.

    Monitoring, Logging, and Uptime

    Agents fail differently than normal web services — they can loop, hallucinate a tool call that doesn’t exist, or silently stall waiting on a model response. Standard uptime monitoring isn’t enough on its own. You need:

  • Uptime checks on the agent API endpoint and the model backend independently, so you know which layer failed.
  • Loop and cost alerts — track token usage per task and alert if a single run exceeds a threshold, which usually indicates a runaway loop.
  • Structured logging of every tool call, its inputs, and its output, so you can reconstruct exactly what an agent did after the fact.
  • Latency percentiles, not just averages — agent tasks have high variance, and p95/p99 latency tells you far more than a mean.
  • We run our own agent workloads with BetterStack for uptime and log aggregation — it’s a solid fit here because it handles both incident alerting and structured log search without stitching together three separate tools, and our self-hosted monitoring walkthrough goes deeper into wiring it up alongside Prometheus for container-level metrics.

    Security and Compliance Considerations

    Treat agent tool access the same way you’d treat a service account’s IAM permissions — least privilege, scoped, and audited.

  • Never give an agent’s shell tool access to a production host directly; route it through a constrained execution sandbox.
  • Log every prompt and every tool call with enough context to reconstruct an incident during a post-mortem.
  • Rate-limit and cost-cap the model backend so a misbehaving agent can’t run up a bill or hammer an upstream API.
  • Put the agent’s public-facing endpoint behind a WAF and DDoS protection — Cloudflare is a common choice here since it also handles TLS termination for the reverse proxy layer.
  • Review the OWASP Top 10 for LLM Applications before going to production; prompt injection and excessive agency are the two risks most teams underestimate.
  • If your business handles regulated data, map your agent’s tool permissions against your compliance framework before launch, not after. Retrofitting audit logging into an agent that’s already handling customer data is far more painful than building it in from day one.

    Scaling on Cloud Infrastructure

    Once the stack is stable locally, moving it to a VPS is mostly a matter of resources. GPU-backed instances are pricier but let you run larger local models; CPU-only instances work fine if you’re proxying to a hosted model API instead of self-hosting the LLM itself.

  • DigitalOcean is a good default for teams that want managed Kubernetes or simple droplets with predictable pricing and fast provisioning.
  • Hetzner offers some of the best price-to-performance on dedicated and cloud servers in Europe, which matters if your agent workloads are compute-heavy and running continuously.
  • Either way, put your agent behind a reverse proxy with TLS from day one, and keep the model backend on an internal network, never exposed directly to the internet.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Do AI agents for business need a GPU to run?
    Not always. Smaller quantized models run fine on CPU for classification, extraction, and routing tasks. GPUs matter when you need larger models for complex reasoning or code generation, or when latency under load is critical.

    Is it safer to self-host or use a hosted agent API?
    Self-hosting gives you more control over data residency and tool permissions, but you own the operational burden — patching, scaling, and monitoring. Hosted APIs shift that burden to the vendor but require you to trust their data handling. Choose based on your compliance requirements, not just cost.

    How do I stop an agent from looping forever?
    Set a hard step limit and a token budget per task at the orchestration layer, not just in the prompt. Combine that with cost alerts so a runaway loop triggers a page before it becomes a bill.

    Can AI agents for business replace existing automation tools?
    Usually not entirely. Agents are best for tasks with ambiguous inputs that benefit from reasoning — triage, summarization, drafting. Deterministic workflows (payroll runs, scheduled reports) are still better served by traditional automation because they’re cheaper and more predictable.

    What’s the biggest mistake teams make deploying their first agent?
    Giving the agent too much tool access too early. Start with read-only tools, add write access incrementally, and gate anything irreversible behind human approval until you have solid logging and enough production history to trust the failure modes.

    How much does it cost to run a self-hosted agent stack?
    For a small internal agent handling a few hundred tasks a day on a CPU-only VPS, expect somewhere in the $20–80/month range for infrastructure alone, excluding any hosted model API costs if you’re not running the LLM locally. GPU-backed setups for larger models scale up from there depending on instance size and uptime.

    Deploying AI agents for business isn’t fundamentally different from deploying any other production service — it just adds a nondeterministic decision-making layer on top of your usual container, networking, and monitoring stack. Get the boring parts right — scoped permissions, structured logging, cost caps, and real uptime monitoring — and the agent layer becomes far easier to trust in production.

  • AI Agents Companies: Top Platforms for DevOps 2026

    AI Agents Companies: A DevOps Guide to Evaluating and Self-Hosting Agent Platforms

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    Autonomous AI agents have moved from research demos to production workloads faster than most infrastructure teams expected. If you’re a developer or sysadmin trying to figure out which AI agents companies are worth building on — and whether to run their stack yourself or hand it off to a managed service — this guide is for you. We’ll cover the current landscape, the tradeoffs between self-hosted and managed platforms, and give you working Docker configs to stand up an agent runtime on your own VPS.

    Why AI Agents Companies Matter for DevOps Teams

    An “AI agent” is software that can plan, call tools, and take multi-step actions toward a goal without a human clicking through every step. The companies building these platforms fall into two broad camps: infrastructure providers that give you the raw orchestration layer (LangChain, Microsoft AutoGen, CrewAI), and model providers that expose agentic capability directly through their APIs (Anthropic, OpenAI). Understanding which camp a vendor sits in matters more than the marketing copy, because it determines how much operational burden lands on your team.

    If you pick a pure orchestration framework, you’re responsible for hosting, scaling, logging, and securing the runtime — the framework just wires together the logic. If you pick a fully managed agent platform, you trade control for convenience and usually pay per-execution pricing that can balloon fast under heavy tool-calling workloads.

    The Rise of Autonomous Agent Platforms

    The shift started when function-calling and structured tool use became reliable enough that agents could chain API calls without constant human correction. Companies like LangChain built developer tooling around this early, followed by Microsoft’s AutoGen for multi-agent conversation patterns, and CrewAI for role-based agent teams. On the model side, Anthropic and OpenAI both ship native agentic tool-use APIs, which means you can skip a third-party orchestration layer entirely for simpler use cases.

    For DevOps teams, the practical question isn’t “which company has the flashiest demo” — it’s “which stack can I deploy, monitor, and roll back like any other production service.” That’s the lens we’ll use throughout this article.

    Self-Hosted vs Managed AI Agents Companies

    Here’s the tradeoff in plain terms:

  • Self-hosted (LangChain, AutoGen, CrewAI, open-source runtimes): Full control over data, logging, and cost. You own the ops burden — scaling, patching, secrets management, uptime.
  • Managed (vendor-hosted agent platforms): Faster to ship, someone else handles scaling and uptime, but you’re locked into their pricing model, rate limits, and data-handling policies.
  • Hybrid: Run your own orchestration layer (LangChain/AutoGen) on a VPS you control, but call out to managed model APIs (Anthropic, OpenAI) for the actual inference. This is the most common pattern we see in production DevOps environments today.
  • Most teams we’ve worked with land on hybrid, because it keeps infrastructure costs predictable while avoiding the operational nightmare of self-hosting large language models.

    Top AI Agents Companies to Evaluate in 2026

    A quick rundown of the platforms worth putting on your shortlist, organized by what they’re actually good at:

  • LangChain / LangGraph — best for building custom agent workflows with fine-grained control over state and tool routing.
  • Microsoft AutoGen — strong for multi-agent conversations where agents need to critique and refine each other’s output.
  • CrewAI — role-based agent orchestration, good fit if you’re modeling a team of specialized agents (researcher, writer, reviewer).
  • Anthropic (Claude API) — native agentic tool use with strong reliability on long-running multi-step tasks.
  • OpenAI (Assistants/Agents API) — mature ecosystem, broad plugin support, tightly integrated with their function-calling spec.
  • Open-Source vs Proprietary AI Agent Platforms

    Open-source frameworks like LangChain and AutoGen give you the source code, which means you can audit exactly what’s being sent to third-party APIs — critical if you’re handling sensitive data. Proprietary managed platforms abstract that away, which is fine for prototyping but can become a compliance headache once you’re processing customer data through an agent you can’t fully inspect.

    If your organization has any regulatory requirements around data residency or auditability, default to self-hosting the orchestration layer even if you’re calling a managed model API underneath. You get the audit trail without needing to run your own inference cluster.

    Deploying an AI Agent Stack on Your Own VPS

    Let’s get concrete. Below is a minimal but production-viable setup for running a LangChain-based agent service behind a reverse proxy, with logging and restart policies configured — the same way you’d deploy any other containerized service. If you haven’t containerized a service like this before, our Docker Compose guide covers the basics in more depth.

    Docker Compose Example for a Self-Hosted Agent Runtime

    # docker-compose.yml
    version: "3.9"
    
    services:
      agent-runtime:
        build: ./agent-runtime
        container_name: agent-runtime
        restart: unless-stopped
        environment:
          - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
          - AGENT_LOG_LEVEL=info
          - MAX_TOOL_CALLS_PER_RUN=15
        ports:
          - "127.0.0.1:8080:8080"
        volumes:
          - ./logs:/app/logs
        deploy:
          resources:
            limits:
              memory: 1g
              cpus: "1.0"
    
      caddy:
        image: caddy:2-alpine
        container_name: agent-proxy
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./Caddyfile:/etc/caddy/Caddyfile
          - caddy_data:/data
    
    volumes:
      caddy_data:

    A minimal Python entrypoint for the agent-runtime service using LangChain:

    # agent-runtime/main.py
    import os
    from fastapi import FastAPI, HTTPException
    from langchain_anthropic import ChatAnthropic
    from langchain.agents import AgentExecutor, create_tool_calling_agent
    from langchain_core.prompts import ChatPromptTemplate
    
    app = FastAPI()
    
    llm = ChatAnthropic(
        model="claude-sonnet-5",
        api_key=os.environ["ANTHROPIC_API_KEY"],
        max_tokens=1024,
    )
    
    prompt = ChatPromptTemplate.from_messages([
        ("system", "You are a DevOps assistant agent with access to tools."),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ])
    
    @app.post("/run")
    async def run_agent(payload: dict):
        task = payload.get("task")
        if not task:
            raise HTTPException(status_code=400, detail="Missing 'task' field")
        response = llm.invoke(task)
        return {"result": response.content}

    Bring it up with:

    docker compose up -d --build
    docker compose logs -f agent-runtime

    Note the memory and CPU limits in the compose file — agent workloads can spike unpredictably during long tool-calling chains, and an unbounded container will happily eat your whole VPS. Set hard limits from day one.

    Monitoring and Securing Your AI Agents Infrastructure

    Agents that call external tools and APIs are a different security surface than a normal web app. A misconfigured agent can leak API keys into logs, make unbounded outbound requests, or get prompt-injected into taking actions you didn’t intend. Treat this the same way you’d treat any service with outbound network access: least privilege, rate limits, and centralized logging.

    Logging, Rate Limiting, and Cost Control

    Practical steps we recommend for any self-hosted agent deployment:

  • Cap MAX_TOOL_CALLS_PER_RUN (or equivalent) so a runaway agent loop can’t rack up API costs overnight.
  • Ship logs to a centralized system rather than trusting local container logs — if you don’t already have log aggregation in place, see our self-hosted monitoring stack guide.
  • Never let the agent process hold long-lived cloud credentials directly; scope API keys narrowly and rotate them.
  • Run the agent container as a non-root user and apply the same Linux server hardening practices you’d use for any internet-facing service.
  • Set outbound firewall rules so the agent can only reach the APIs it actually needs, not the entire internet.
  • For uptime monitoring on the agent endpoint itself, a lightweight external checker like BetterStack will alert you the moment the runtime stops responding — useful since agent processes can hang silently on a stuck tool call in a way a normal web request wouldn’t.

    Choosing the Right Infrastructure Provider

    Agent workloads are bursty — mostly idle, then a spike of CPU and memory during a multi-step run. A VPS with predictable flat-rate pricing beats pure serverless here, since agent runs can take minutes rather than seconds and serverless cold starts add latency to every tool call.

    We’ve run agent stacks like the one above on both DigitalOcean droplets and Hetzner cloud servers. DigitalOcean’s managed load balancers make it easy to scale the proxy layer horizontally if you’re running multiple agent replicas; Hetzner is the better pick if you’re cost-sensitive and want more RAM per dollar for the container memory limits mentioned above. Either way, put Cloudflare in front of the public endpoint — it absorbs abusive traffic before it ever reaches your agent runtime, which matters a lot when a single malicious request can trigger an expensive multi-step tool chain.

    Putting It All Together

    The AI agents companies landscape is consolidating around a hybrid pattern: self-hosted orchestration for control and auditability, managed model APIs for the actual inference. That split gives DevOps teams the best of both worlds — you’re not running your own GPU cluster, but you retain full visibility into what the agent is doing and full control over the deployment lifecycle. Start small: one containerized agent service, hard resource limits, centralized logging, and a firewall that only allows the outbound calls you actually need. Scale from there once you understand your real usage patterns and cost profile.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    What’s the difference between an AI agent and a chatbot?
    A chatbot responds to messages. An AI agent plans multi-step actions, calls external tools or APIs, and can act autonomously toward a goal without a human approving each step.

    Should I self-host my agent orchestration layer or use a managed platform?
    Self-host if you need auditability, data residency control, or predictable costs. Use a managed platform if you’re prototyping and want to ship fast without owning the ops burden.

    Which AI agents companies are best for enterprise compliance requirements?
    Open-source frameworks like LangChain and Microsoft AutoGen give you full visibility into what data leaves your infrastructure, which is usually easier to justify to a compliance team than a fully black-box managed platform.

    How do I prevent an agent from racking up huge API costs?
    Set a hard cap on tool calls per run, enforce request timeouts, and monitor per-run token usage. Treat cost limits the same way you’d treat rate limiting on any public API.

    Can I run an agent stack on a small VPS?
    Yes — the orchestration layer itself is lightweight. A 2GB-4GB RAM VPS is enough for moderate traffic as long as you’re calling out to a managed model API rather than hosting your own LLM inference.

    Do I need Kubernetes to run AI agents in production?
    Not for most teams. A single VPS with Docker Compose, proper resource limits, and a reverse proxy handles low-to-moderate traffic fine. Move to Kubernetes only once you need multi-node scaling or zero-downtime rolling deploys across several agent replicas.

  • AI Agents Company: A DevOps Guide to Evaluation

    How to Evaluate an AI Agents Company Before You Deploy in Production

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    Every vendor pitch deck now claims to be an “ai agents company.” Strip away the marketing and what you’re actually buying is a runtime, an orchestration layer, and a set of API integrations sitting on top of an LLM. As a sysadmin or platform engineer, your job is to evaluate that stack the same way you’d evaluate any other piece of production infrastructure: uptime guarantees, data egress, observability, and exit costs.

    What an AI Agents Company Actually Sells You

    Strip the branding and every “agent platform” is composed of four layers:

  • Orchestration — a loop that decides which tool to call next (ReAct, plan-and-execute, or a custom state machine)
  • Tool/function execution — sandboxed code execution, API calls, browser automation
  • Memory — vector store or key-value store for context persistence
  • Model routing — calls to one or more LLM providers, sometimes with fallback logic
  • When a vendor says “proprietary agent framework,” they usually mean a wrapper around LangChain or a similar open-source project, plus a hosted UI. That’s not necessarily bad — but it changes your leverage in negotiations and your disaster-recovery options if the company shuts down or changes pricing.

    Questions to Ask Before Signing a Contract

    Before you commit budget to any ai agents company, get concrete answers on these points:

  • Where is agent execution actually running — their cloud, yours, or a shared multi-tenant environment?
  • Can you export conversation logs and memory stores in an open format?
  • What happens to in-flight agent runs during a provider outage?
  • Is there a self-hosted or on-prem tier, and what does it cost relative to the SaaS tier?
  • Who owns the data used for fine-tuning, if any?
  • If you can’t get a straight answer on data residency, treat that as a red flag — not just for compliance, but because it tells you how mature their own infrastructure practices are.

    Self-Hosting vs. Managed AI Agent Platforms

    For teams with existing DevOps capacity, self-hosting an open agent stack is often cheaper and gives you full control over logging, secrets, and network egress. The tradeoff is that you own patching, scaling, and monitoring. If your team is already comfortable running containerized services — the kind of workload we cover in our Docker Compose production guide — self-hosting an agent framework isn’t a huge leap.

    Managed platforms win when you need agent capability shipped fast and don’t have spare ops headcount. But recalculate that tradeoff every 6-12 months; hosted agent pricing scales with token volume and tool calls in ways that surprise teams who scoped for a pilot, not production traffic.

    Deploying an Open-Source Agent Stack with Docker

    Here’s a minimal, production-leaning setup using an open agent runtime, a vector store for memory, and Redis for task queuing. This mirrors what most “agent platforms” run under the hood.

    # docker-compose.yml
    version: "3.9"
    services:
      agent-runtime:
        image: ghcr.io/your-org/agent-runtime:latest
        restart: unless-stopped
        environment:
          - OPENAI_API_KEY=${OPENAI_API_KEY}
          - VECTOR_DB_URL=http://qdrant:6333
          - REDIS_URL=redis://redis:6379/0
        depends_on:
          - qdrant
          - redis
        ports:
          - "8080:8080"
        deploy:
          resources:
            limits:
              cpus: "2"
              memory: 2G
    
      qdrant:
        image: qdrant/qdrant:latest
        restart: unless-stopped
        volumes:
          - qdrant-data:/qdrant/storage
    
      redis:
        image: redis:7-alpine
        restart: unless-stopped
        command: ["redis-server", "--appendonly", "yes"]
        volumes:
          - redis-data:/data
    
    volumes:
      qdrant-data:
      redis-data:

    Bring it up with:

    docker compose up -d
    docker compose logs -f agent-runtime

    Set resource limits from day one. Agent workloads that spawn subprocess-based tools (browser automation, code execution sandboxes) can spike memory unpredictably, and an unbounded container will eventually take down neighboring services on the same host.

    Locking Down Tool Execution

    If your agents execute arbitrary code or shell commands as a “tool,” that container needs to be treated like an untrusted workload:

    docker run --rm 
      --read-only 
      --network=none 
      --memory=512m 
      --pids-limit=64 
      --security-opt=no-new-privileges 
      sandbox-executor:latest

    No network access, read-only filesystem, and a PID limit are the minimum bar. If the agent needs outbound network access for a specific API, use an egress allowlist via a proxy sidecar rather than opening the container to the internet directly. We go deeper on isolating untrusted workloads in our Linux server hardening guide.

    Monitoring and Observability for AI Agent Workloads

    Agent systems fail differently than typical web services — you’ll see infinite tool-call loops, silent hallucinated outputs, and slow degradation from rate-limited upstream APIs rather than clean crashes. Standard uptime checks won’t catch most of this.

    At minimum, instrument:

  • Token usage per request — catches runaway loops before they blow your API bill
  • Tool call latency — flags a degraded upstream API before users notice
  • Task completion rate — the agent-specific equivalent of an error rate
  • Queue depth — if you’re using Redis or a similar broker, growing queue depth means your workers can’t keep up
  • A basic Prometheus scrape config for the runtime above:

    scrape_configs:
      - job_name: 'agent-runtime'
        static_configs:
          - targets: ['agent-runtime:8080']
        metrics_path: /metrics
        scrape_interval: 15s

    For alerting on top of this, a hosted uptime and incident tool saves you from building your own paging pipeline. BetterStack is a solid option if you want status pages and on-call alerting without standing up your own Alertmanager stack — worth it once agents are running anything customer-facing.

    Where to Host the Stack

    Agent runtimes with a vector database and Redis are lightweight enough to run on a single well-specced VPS for small to mid-size workloads — you don’t need Kubernetes to get started, contrary to what most vendor blog posts imply. A 4-8 vCPU droplet with SSD storage from DigitalOcean handles a moderate agent workload comfortably, and you can scale to a managed Kubernetes cluster later if tool-call volume actually justifies it. If you’re serving agent APIs to external clients, put Cloudflare in front for DDoS protection and to keep your origin IP off the public internet.

    Cost Comparison: Managed Platform vs. Self-Hosted

    A rough monthly comparison for a team running ~500K agent invocations:

  • Managed ai agents company platform: $2,000-8,000/mo depending on seat count and token overage fees
  • Self-hosted on a VPS: $80-200/mo for compute, plus your LLM API token costs (same either way), plus engineering time for maintenance
  • The self-hosted number looks like an obvious win until you price in the engineer-hours spent on patching, scaling, and incident response. For a team of one or two, the managed platform is often cheaper in practice. For a team with an existing platform engineer, self-hosting usually wins within two to three months.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    What’s the difference between an AI agent and a chatbot?
    A chatbot responds to messages. An agent decides which tools to call, executes multi-step plans, and can act without a human approving each step — that autonomy is exactly what needs the extra monitoring and sandboxing covered above.

    Do I need Kubernetes to run an AI agent stack?
    No. A single VPS with Docker Compose handles moderate workloads fine. Move to Kubernetes when you need multi-region failover or horizontal autoscaling based on queue depth, not before.

    How do I evaluate an ai agents company’s security posture?
    Ask for their SOC 2 report, ask exactly where agent execution happens, and ask what network access their tool-execution sandbox has by default. Vague answers on any of these are a red flag.

    Can I switch between managed and self-hosted later?
    Yes, if you chose a platform built on open standards (LangChain, OpenAI-compatible APIs) from the start. Proprietary agent DSLs make migration expensive — factor that lock-in risk into your initial vendor decision.

    What’s the biggest cost surprise with hosted agent platforms?
    Token and tool-call overage fees once you move from pilot to production traffic. Model your expected invocation volume before signing, not after the first invoice.

    Is self-hosting more secure than a managed platform?
    Not automatically — it’s only more secure if you actually apply the same hardening (network isolation, resource limits, patching cadence) a competent vendor would apply. Self-hosting without that discipline is worse, not better.

  • Docker Compose Volumes: The Complete Setup Guide

    Docker Compose Volumes: A Practical Guide to Persistent Data

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    If you’ve ever restarted a container and watched your database vanish into thin air, you already understand why docker compose volumes matter. Containers are ephemeral by design — anything written inside a container’s writable layer disappears the moment that container is removed. Volumes are Docker’s answer to this problem, and getting them right is one of the first things you need to master before running anything stateful in production.

    This guide covers everything you need to know about docker compose volumes: named volumes, bind mounts, tmpfs mounts, permissions, backups, and the common mistakes that cause data loss. We’ll use real, runnable docker-compose.yml examples throughout.

    Why Volumes Exist

    Docker containers are meant to be disposable. You should be able to destroy a container and recreate it from its image without worrying about what happens to your application’s state. But that raises an obvious problem: where does your database, your uploaded files, or your configuration live if the container itself is throwaway?

    The answer is volumes — storage that exists independently of the container lifecycle. Docker supports three main mechanisms for persisting data:

  • Named volumes — managed by Docker, stored under /var/lib/docker/volumes/ on the host
  • Bind mounts — a direct mapping between a host path and a container path
  • tmpfs mounts — in-memory storage that never touches disk, useful for secrets or caches
  • Each has different tradeoffs, and Compose makes it easy to declare any of them declaratively in a single YAML file.

    Named Volumes: The Default Choice

    Named volumes are the recommended approach for most production workloads. Docker manages the storage location for you, and volumes persist independently of any container — even if you run docker compose down, the volume survives unless you explicitly pass -v or --volumes.

    Here’s a basic example using PostgreSQL:

    services:
      db:
        image: postgres:16
        environment:
          POSTGRES_PASSWORD: examplepass
        volumes:
          - pgdata:/var/lib/postgresql/data
        ports:
          - "5432:5432"
    
    volumes:
      pgdata:

    Notice the two-part structure: the volumes: key under the service maps a named volume (pgdata) to a path inside the container. The top-level volumes: block declares that pgdata exists and lets Compose create it if it doesn’t already.

    You can inspect the volume directly:

    docker volume inspect pgdata
    docker volume ls

    Named volumes are portable across host filesystems, get automatic driver support (local, NFS, cloud-backed drivers), and are the correct choice when you don’t need to browse the files from the host directly.

    Bind Mounts: Direct Host Access

    Bind mounts map a specific directory or file on the host machine into the container. They’re most useful during development, when you want code changes on your host to reflect immediately inside the container without rebuilding the image.

    services:
      app:
        build: .
        volumes:
          - ./src:/usr/src/app
          - ./config/nginx.conf:/etc/nginx/nginx.conf:ro
        ports:
          - "3000:3000"

    The :ro suffix mounts the file read-only inside the container, which is a good habit for configuration files you don’t want the app accidentally overwriting. Bind mounts are also the standard way to expose the Docker socket for tools that need to manage containers, though that comes with security implications worth understanding before you do it in a shared environment — see Docker’s own guidance on socket security if you’re considering it.

    The downside of bind mounts is portability: the host path has to exist and be correct on every machine that runs the Compose file, which makes them a poor fit for production deployments across different servers.

    tmpfs Mounts: Ephemeral In-Memory Storage

    tmpfs mounts never persist to disk. They’re wiped when the container stops, which makes them ideal for temporary secrets, session data, or scratch space that shouldn’t leak onto the host filesystem.

    services:
      cache:
        image: redis:7
        tmpfs:
          - /data

    This is a niche use case compared to named volumes and bind mounts, but it’s worth knowing it exists — especially for compliance-sensitive workloads where you need a guarantee that certain data never touches persistent storage.

    A Real-World Multi-Service Example

    Here’s a more complete example combining a web app, a database, and a reverse proxy — a common pattern for anything you’d deploy on a VPS:

    services:
      web:
        build: .
        volumes:
          - ./app:/usr/src/app
          - static_files:/usr/src/app/static
        depends_on:
          - db
    
      db:
        image: postgres:16
        volumes:
          - pgdata:/var/lib/postgresql/data
        environment:
          POSTGRES_PASSWORD: examplepass
    
      nginx:
        image: nginx:alpine
        volumes:
          - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
          - static_files:/usr/src/app/static:ro
        ports:
          - "80:80"
    
    volumes:
      pgdata:
      static_files:

    This pattern shares the static_files volume between the web and nginx services, so nginx can serve static assets directly without proxying every request through the app server. If you’re setting up a similar stack on your own infrastructure, our guide on deploying Docker Compose to a production VPS walks through the full process including firewall and TLS setup.

    Permissions and Ownership Gotchas

    One of the most common issues developers hit with docker compose volumes is file permission mismatches. If your container runs as a non-root user (which it should, for security reasons), files written by that user inside a bind-mounted directory will be owned by the corresponding UID on the host — which may not match any real user on your system.

    A few practical fixes:

  • Match the container’s UID to your host user with the user: directive in Compose
  • Use a startup script that runs chown on the mounted directory before dropping privileges
  • For named volumes, let Docker manage ownership — it’s usually less painful than bind mounts for this exact reason
  • If you’re chasing a permission denied error, check docker compose exec <service> id and compare it against the ownership of the files with ls -la on the host. Mismatched UIDs are the cause more often than any actual bug in your application.

    Backing Up and Restoring Volumes

    Since named volumes live outside your source-controlled Compose file, you need a separate backup strategy. The simplest approach uses a throwaway container to tar up the volume contents:

    docker run --rm 
      -v pgdata:/data 
      -v $(pwd):/backup 
      alpine 
      tar czf /backup/pgdata-backup.tar.gz -C /data .

    To restore, reverse the process:

    docker run --rm 
      -v pgdata:/data 
      -v $(pwd):/backup 
      alpine 
      tar xzf /backup/pgdata-backup.tar.gz -C /data

    For anything running in production, don’t rely on manual backups alone. Automated monitoring that alerts you before a disk fills up or a backup job silently fails is worth the setup time — BetterStack offers uptime and log monitoring that integrates cleanly with Dockerized stacks if you want alerts wired up without building your own tooling from scratch.

    Choosing the Right Storage Backend for Your Host

    Where your volumes physically live matters more than most people realize until they hit an I/O bottleneck. If you’re running database-heavy workloads in Compose, the underlying disk speed of your VPS provider directly affects query latency. Providers like Hetzner and DigitalOcean both offer NVMe-backed instances that handle volume-heavy workloads noticeably better than budget shared hosting — worth considering if you’re scaling past a hobby project. If your Compose stack sits behind a domain and you want DDoS protection and caching in front of it, Cloudflare is a standard, low-friction addition to the stack.

    Common Mistakes to Avoid

  • Forgetting the top-level volumes: declaration — Compose will still create an anonymous volume if you skip it, but you lose the ability to reference it by name elsewhere
  • Using bind mounts for production databases — path mismatches between environments cause subtle, hard-to-debug failures
  • Running docker compose down -v without thinking — this deletes named volumes along with the containers, which has destroyed more than one person’s local database
  • Not setting :ro on config files — an app with a bug that writes to its own config file can silently corrupt your setup
  • Ignoring volume drivers — if you need multi-host storage (NFS, cloud block storage), you need a volume driver configured; the default local driver only works on a single host
  • If you’re moving from a single-container setup to a full multi-service stack, it’s also worth reading through our breakdown of Docker networking modes, since volumes and networking decisions often need to be made together when you’re designing how services talk to each other and to the outside world.

    Wrapping Up

    Docker Compose volumes aren’t complicated once you understand the distinction between named volumes, bind mounts, and tmpfs — the hard part is picking the right one for each use case and avoiding the permission and lifecycle mistakes that catch people off guard. For production database storage, default to named volumes. For local development where you need live code reloading, bind mounts make sense. Reserve tmpfs for genuinely ephemeral data that should never touch disk.

    Get comfortable inspecting volumes with docker volume ls and docker volume inspect, build a backup habit early, and pay attention to the underlying disk performance of whatever host you’re running on — it matters more than most Compose tutorials mention.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Q: What’s the difference between a named volume and a bind mount?
    A: A named volume is managed entirely by Docker and stored in Docker’s own directory structure on the host, while a bind mount maps a specific host path directly into the container. Named volumes are more portable; bind mounts give you direct host-side access to the files.

    Q: Does docker compose down delete my volumes?
    A: No, by default docker compose down stops and removes containers but leaves named volumes intact. You have to explicitly add the -v flag (docker compose down -v) to remove volumes as well.

    Q: Can I share one volume between multiple services in the same Compose file?
    A: Yes. Declare the volume once in the top-level volumes: block and reference it in the volumes: section of each service that needs it, as shown in the nginx/web example above.

    Q: Why do I get permission denied errors when writing to a mounted volume?
    A: This usually happens because the UID the container process runs as doesn’t match the ownership of the files on the host. Check with docker compose exec <service> id and adjust ownership or the container’s user: directive accordingly.

    Q: How do I back up a named volume?
    A: Run a temporary container that mounts the volume alongside a host directory, then use tar to archive the volume’s contents to that host directory, as shown in the backup example above.

    Q: Should I use volumes for stateless services?
    A: Generally no. If a service doesn’t need to retain data across restarts, skip the volume entirely and let the container’s writable layer handle it — adding a volume you don’t need just adds cleanup overhead later.

  • AI Phone Agent: Self-Host One with Docker & DevOps Tools

    How to Build a Self-Hosted AI Phone Agent with Docker

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    If you’ve shopped around for a hosted AI phone agent platform, you’ve probably noticed the per-minute pricing adds up fast once you’re handling real call volume. For developers and sysadmins who already run Docker infrastructure, self-hosting an AI phone agent is a realistic alternative — you control the stack, the data, and the cost.

    This guide walks through the architecture, the Docker Compose stack, telephony integration, and deployment to a VPS, so you can run your own AI phone agent instead of paying per-minute SaaS fees.

    What Is an AI Phone Agent?

    An AI phone agent is a voice pipeline that answers or places phone calls, transcribes speech in real time, feeds that transcript to a language model, and speaks the model’s response back to the caller. Under the hood it’s four components glued together:

  • Telephony layer — receives/places PSTN calls (Twilio, SIP trunk, or Asterisk)
  • Speech-to-text (STT) — converts caller audio to text in near real time
  • LLM orchestration — generates a response, optionally calling tools/APIs
  • Text-to-speech (TTS) — converts the LLM’s reply back into audio
  • Each of these can be a hosted API or an open-source model running in a container. The self-hosted approach swaps API calls for local inference where it makes sense, and keeps everything else containerized for reproducibility.

    Why Self-Host Instead of Using a SaaS Platform

    The economics change quickly at volume. A SaaS AI phone agent platform typically charges $0.05–$0.15 per minute on top of telephony carrier costs. If you’re running a support line or an outbound campaign doing thousands of minutes a month, that’s real money. Self-hosting shifts the cost to a fixed VPS bill plus carrier minutes, and gives you full control over latency, logging, and model choice.

    The tradeoff is operational complexity — you’re now responsible for uptime, scaling, and monitoring, which is exactly the kind of work a DevOps-oriented reader is already comfortable with. If you haven’t containerized a networked service before, it’s worth reading our Docker networking basics guide first, since the telephony and STT containers need to talk to each other reliably.

    Core Architecture

    A minimal self-hosted stack looks like this:

    Caller ↔ SIP Trunk ↔ Asterisk (container) ↔ Audio bridge (WebSocket)
                                                       ↓
                                            STT service (Whisper, container)
                                                       ↓
                                            LLM orchestrator (container)
                                                       ↓
                                            TTS service (Piper/Coqui, container)
                                                       ↓
                                            Back to Asterisk → Caller

    Each box is its own container, connected over an internal Docker network. This separation matters operationally: you can scale STT and TTS independently, swap the LLM provider without touching telephony, and restart any single component without dropping the whole stack.

    Setting Up the Stack with Docker

    Start with a docker-compose.yml that defines the core services. This example uses Asterisk for SIP telephony, OpenAI’s Whisper for STT, a lightweight FastAPI orchestrator for the LLM logic, and Piper for TTS.

    version: "3.9"
    services:
      asterisk:
        image: andrius/asterisk:18
        ports:
          - "5060:5060/udp"
          - "10000-10100:10000-10100/udp"
        volumes:
          - ./asterisk-config:/etc/asterisk
        networks:
          - agent-net
    
      stt:
        build: ./stt
        environment:
          - MODEL_SIZE=small
        networks:
          - agent-net
    
      orchestrator:
        build: ./orchestrator
        environment:
          - LLM_API_KEY=${LLM_API_KEY}
        depends_on:
          - stt
          - tts
        networks:
          - agent-net
    
      tts:
        build: ./tts
        networks:
          - agent-net
    
    networks:
      agent-net:
        driver: bridge

    The orchestrator container is the piece you’ll customize most. A minimal version using FastAPI to bridge transcripts to an LLM might look like this:

    # orchestrator/main.py
    from fastapi import FastAPI, WebSocket
    import httpx
    import os
    
    app = FastAPI()
    LLM_API_KEY = os.environ["LLM_API_KEY"]
    
    @app.websocket("/call")
    async def handle_call(websocket: WebSocket):
        await websocket.accept()
        async with httpx.AsyncClient() as client:
            while True:
                transcript = await websocket.receive_text()
                response = await client.post(
                    "https://api.openai.com/v1/chat/completions",
                    headers={"Authorization": f"Bearer {LLM_API_KEY}"},
                    json={
                        "model": "gpt-4o-mini",
                        "messages": [{"role": "user", "content": transcript}],
                    },
                )
                reply = response.json()["choices"][0]["message"]["content"]
                await websocket.send_text(reply)

    This is deliberately minimal — no retry logic, no conversation memory — but it’s enough to prove the pipeline end-to-end before you add production concerns like call state, interruption handling, and logging.

    Connecting Telephony (Twilio or SIP Trunk)

    You have two realistic options for the telephony leg:

  • Twilio Media Streams — easiest to set up, no SIP trunk needed, pay-per-minute carrier cost only. Twilio streams raw audio to a WebSocket endpoint you control, which is a natural fit for the orchestrator above. See Twilio’s Media Streams docs for the exact WebSocket payload format.
  • Self-hosted Asterisk + SIP trunk — more setup work (NAT traversal, SIP trunk provider, dialplan config) but no per-vendor markup beyond the trunk provider’s per-minute rate, and full control over call routing.
  • For most teams starting out, Twilio Media Streams is the faster path — you skip SIP/NAT headaches entirely and can have a working prototype in an afternoon. Move to Asterisk once call volume justifies the added ops burden.

    Deploying to a VPS

    Audio streaming is latency-sensitive, so where you deploy matters. A few practical guidelines:

  • Pick a region close to your majority caller base — round-trip latency over 300ms causes audible lag in the conversation.
  • Use a VPS with at least 4 vCPUs / 8GB RAM if you’re running Whisper locally instead of an API; STT is the most CPU/GPU-hungry piece of the stack.
  • Open only the UDP ports Asterisk needs (SIP signaling + RTP range) and put everything else behind a private Docker network.
  • Terminate TLS/WSS at a reverse proxy in front of the orchestrator’s WebSocket endpoint — never expose it in plaintext.
  • If you haven’t hardened a fresh VPS before running production telephony workloads on it, check our VPS hardening checklist before opening the required ports to the internet.

    For the actual host, DigitalOcean droplets and Hetzner cloud servers are both solid picks — Hetzner tends to be cheaper per vCPU, DigitalOcean has slightly better network peering in North America. Either works fine for a stack this size; pick based on where your callers are.

    Monitoring and Scaling

    A phone agent that silently drops calls is worse than no phone agent at all, so monitoring isn’t optional here. At minimum, track:

  • Call setup success rate (SIP 200 OK vs failures)
  • STT latency per utterance
  • LLM response latency
  • TTS generation time
  • Container restart counts
  • BetterStack is a reasonable option for uptime and log monitoring across the whole stack — you can point it at your Docker container logs and get alerted the moment the orchestrator starts throwing errors, rather than finding out from an angry caller. Pair it with basic container health checks in your docker-compose.yml so Docker itself restarts a crashed STT or TTS container automatically.

    Once a single instance is stable, scaling out is mostly a matter of running multiple orchestrator + STT + TTS replicas behind a load balancer, with Asterisk (or Twilio) distributing calls across them. If you’re new to running Compose stacks in production, our Docker Compose production guide covers restart policies, health checks, and resource limits in more depth.

    If you’re serving the agent’s web-facing dashboard or webhook endpoints publicly, put Cloudflare in front of it for DDoS protection and TLS termination — telephony webhook endpoints are a common target for probing traffic once they’re discoverable.

    Handling Conversation State

    The minimal orchestrator above treats every transcript as a one-off request, which breaks down the moment a caller references something they said 20 seconds earlier. In production you’ll want:

  • A per-call conversation buffer keyed by call SID, stored in Redis with a short TTL
  • A system prompt that includes the last N turns, not just the current utterance
  • A hard timeout that ends the call gracefully if the LLM doesn’t respond within ~2 seconds, falling back to a canned “let me check on that” filler phrase to avoid dead air
  • Dead air is the single biggest giveaway that a caller is talking to a bot rather than a human, so latency budgeting deserves more attention than the LLM prompt itself.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Q: Do I need a GPU to run an AI phone agent’s speech models?
    A: Not necessarily. Whisper’s smaller models run acceptably on CPU for low call volume, but if you’re handling more than a handful of concurrent calls, a GPU (or a hosted STT API) will keep latency down significantly.

    Q: Is Twilio required, or can I avoid it entirely?
    A: Twilio isn’t required — Asterisk with a SIP trunk provider gives you full self-hosted telephony — but Twilio’s Media Streams API is significantly faster to prototype with and worth using for an initial proof of concept.

    Q: How much does self-hosting actually save compared to a SaaS AI phone agent?
    A: It depends on volume. Below a few thousand minutes a month, a SaaS platform is often cheaper once you factor in your own engineering time. Above that, self-hosting on a $40–80/month VPS usually wins.

    Q: What’s the biggest source of latency in the pipeline?
    A: Usually STT transcription, followed by LLM response time. TTS is typically the fastest step if you’re using a lightweight model like Piper.

    Q: Can this stack handle outbound calls, not just inbound?
    A: Yes — both Asterisk and Twilio support originating calls. The orchestrator logic is identical; only the call-initiation step differs.

    Q: How do I keep call recordings compliant with regulations like TCPA or GDPR?
    A: Store recordings and transcripts encrypted at rest, log consent/disclosure at call start, and set a retention policy that automatically purges data after a defined period — this is a legal question as much as a technical one, so involve counsel for anything customer-facing.

    Self-hosting an AI phone agent isn’t a weekend project, but if you’re already comfortable with Docker and basic Linux ops, none of the individual pieces here are exotic. Start with Twilio Media Streams and a hosted LLM API to validate the concept, then move pieces in-house — Whisper, then TTS, then full SIP — as volume and cost justify it.

  • AI Agent for Business: Self-Hosted Deployment Guide

    How to Deploy an AI Agent for Business on Self-Hosted Infrastructure

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    Every SaaS vendor now sells an “AI agent for business,” usually as a black-box API with a per-seat price tag and zero control over where your data goes. For developers and sysadmins who already run their own stacks, that’s a bad trade. You can run an equally capable agent on a VPS you control, using open-source tooling, for a fraction of the recurring cost — and you get full visibility into logs, prompts, and data flow.

    This guide walks through the architecture, a working Docker Compose deployment, security hardening, and monitoring for a self-hosted AI agent for business use — things like internal support triage, document Q&A, and workflow automation.

    What Is an AI Agent for Business, Really?

    Strip away the marketing and an AI agent is a loop: an LLM receives a goal, decides on an action (call a tool, query a database, hit an API), observes the result, and repeats until the goal is satisfied or it hits a limit. For business use cases, that loop typically wraps around:

  • A knowledge base (tickets, docs, wikis) for retrieval-augmented answers
  • Internal APIs (CRM, billing, inventory) the agent can call
  • A scheduling or trigger mechanism (webhook, cron, Slack event)
  • An audit log, because someone in compliance will ask for one
  • The frameworks doing the heavy lifting — LangChain, LlamaIndex, and CrewAI — are open source and framework-agnostic about which model runs behind them. That’s the leverage point: you’re not locked into a single vendor’s agent product.

    Why Self-Host Instead of Using a SaaS Agent Platform

    Three reasons come up in almost every infrastructure review:

  • Data residency and compliance. If the agent touches customer PII, contracts, or financial data, routing it through a third-party SaaS agent platform adds a data-processing agreement you may not want.
  • Cost at scale. Per-seat or per-run SaaS pricing gets expensive fast once an agent is handling hundreds of internal requests a day. A VPS running a quantized local model or a metered API call to a model provider is usually cheaper past a certain volume.
  • Extensibility. Self-hosted agents can call arbitrary internal tools without waiting on a vendor’s integration roadmap.
  • The tradeoff is operational: you own the uptime, the patching, and the security posture. If you’re already comfortable running Docker containers in production, that overhead is manageable.

    Architecture of a Self-Hosted AI Agent Stack

    A minimal, production-viable stack looks like this:

  • Orchestration layer — Python service running LangChain or a lightweight custom agent loop
  • Model backend — either a hosted API (OpenAI, Anthropic) or a local model served via Ollama
  • Vector store — Qdrant or pgvector for retrieval over internal documents
  • Task queue — Redis or Celery for async agent runs that shouldn’t block a web request
  • Reverse proxy + TLS — Caddy or Nginx in front of the API
  • Keeping each piece in its own container makes the stack portable across any VPS provider and easy to reproduce for staging environments.

    Core Components in a docker-compose.yml

    Here’s a working baseline you can adapt. It runs a local model server, a vector database, Redis for the task queue, and the agent API itself.

    version: "3.9"
    services:
      ollama:
        image: ollama/ollama:latest
        ports:
          - "11434:11434"
        volumes:
          - ollama_data:/root/.ollama
        restart: unless-stopped
    
      qdrant:
        image: qdrant/qdrant:latest
        ports:
          - "6333:6333"
        volumes:
          - qdrant_data:/qdrant/storage
        restart: unless-stopped
    
      redis:
        image: redis:7-alpine
        restart: unless-stopped
    
      agent-api:
        build: ./agent-api
        environment:
          - OLLAMA_HOST=http://ollama:11434
          - QDRANT_HOST=http://qdrant:6333
          - REDIS_URL=redis://redis:6379/0
        ports:
          - "8000:8000"
        depends_on:
          - ollama
          - qdrant
          - redis
        restart: unless-stopped
    
    volumes:
      ollama_data:
      qdrant_data:

    Deploying the Agent API

    The agent-api service is a small FastAPI app that exposes an endpoint for triggering agent runs. A minimal version:

    from fastapi import FastAPI
    from pydantic import BaseModel
    import httpx
    
    app = FastAPI()
    
    class AgentRequest(BaseModel):
        query: str
    
    @app.post("/run")
    async def run_agent(req: AgentRequest):
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                "http://ollama:11434/api/generate",
                json={"model": "llama3", "prompt": req.query, "stream": False},
                timeout=60,
            )
        return resp.json()

    Pull the base model and bring the stack up:

    docker compose up -d
    docker exec -it $(docker compose ps -q ollama) ollama pull llama3
    curl -X POST http://localhost:8000/run 
      -H "Content-Type: application/json" 
      -d '{"query": "Summarize the last 5 support tickets tagged billing"}'

    Step-by-Step Rollout Checklist

    When moving this from a laptop test to something a team relies on, work through this order:

  • Provision a VPS with enough RAM for your model size (7B models need ~8GB, 13B needs ~16GB minimum)
  • Lock down SSH with key-only auth and disable root login
  • Put the agent API behind a reverse proxy with TLS via Let’s Encrypt
  • Add rate limiting so a runaway agent loop can’t hammer downstream APIs or your model bill
  • Wire up centralized logging before you need it, not after an incident
  • If you haven’t hardened a fresh Linux box before, our Linux server hardening checklist covers the baseline steps in more detail.

    Securing an AI Agent for Business Use

    Agents that can call internal tools are effectively privileged automation, and they should be treated with the same scrutiny as a service account. A few non-negotiables:

  • Scope API keys the agent uses to the minimum required permissions — never hand it an admin token
  • Log every tool call and the arguments passed, not just the final response
  • Put a human-approval step in front of any destructive action (refunds, account deletions, mass emails)
  • Sanitize retrieved document content before it’s injected back into the prompt, to reduce prompt-injection risk from untrusted sources
  • The OWASP Top 10 for LLM Applications is worth reading before you connect an agent to anything that writes data, not just reads it.

    Monitoring and Scaling

    Once the agent is handling real traffic, you need visibility into latency, error rates, and cost per run — especially if you’re metering calls to a paid model API. A basic Prometheus + Grafana setup exporting request duration and token counts from the FastAPI service gets you most of the way there; pair it with uptime alerting so you know immediately if the model backend goes down. Our guide on monitoring Docker containers with Prometheus and Grafana walks through wiring up the exporters for a stack like this one.

    For scaling past a single VPS, split the model backend onto its own GPU-backed instance and keep the lightweight agent API and vector store on cheaper compute — there’s no reason to pay for GPU hours to run a FastAPI wrapper.

    Choosing Infrastructure for Your Agent Stack

    The model server is the resource-hungry piece; everything else is lightweight. A sensible split:

  • A CPU-only droplet for the agent API, Redis, and vector store — DigitalOcean droplets work well here and their managed Kubernetes option is a clean upgrade path once you need to scale horizontally
  • A dedicated GPU or high-RAM instance for the model server if you’re self-hosting inference — Hetzner dedicated servers are a cost-effective option for running larger open-weight models continuously
  • External uptime and status-page monitoring so you catch outages before users report them — BetterStack covers this without needing to self-host another monitoring stack
  • Splitting cost centers this way keeps your bill predictable instead of paying GPU rates for idle orchestration containers.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Do I need a GPU to self-host an AI agent for business?
    Not necessarily. Smaller quantized models (7B parameters) run acceptably on CPU with enough RAM, though responses will be slower. For production workloads with real-time expectations, a GPU instance or a hosted model API is usually worth the cost.

    Is self-hosting actually cheaper than a SaaS AI agent platform?
    At low volume, SaaS is often cheaper because you avoid infrastructure overhead. Past a few hundred agent runs per day, self-hosted infrastructure typically wins on cost, especially if you’re using an open-weight model instead of paying per-token for a hosted API.

    Can I use a hosted model API instead of running Ollama locally?
    Yes — swap the ollama service call in the agent API for a request to OpenAI, Anthropic, or another provider’s API. The rest of the architecture (vector store, task queue, tool-calling logic) stays the same.

    How do I stop the agent from taking destructive actions on its own?
    Add an explicit approval gate in the tool-calling layer for any action tagged as destructive, and require a human to confirm before the agent executes it. Never grant the agent’s API credentials write access it doesn’t need.

    What’s the minimum team size where this is worth building?
    If you already run a few production Docker services and have someone comfortable with basic Linux administration, the operational overhead is small. Below that, a managed SaaS agent product is probably the faster path.

    How do I keep the vector store data in sync with our source documents?
    Run a scheduled job (cron or Celery beat) that re-embeds changed documents and upserts them into Qdrant. For most internal knowledge bases, a nightly sync is frequent enough.

    Wrapping Up

    A self-hosted AI agent for business isn’t more complicated than any other containerized service you’re already running — it’s a model backend, a vector store, a task queue, and a thin API tying them together. The real work is in the operational discipline: scoping permissions tightly, logging every tool call, and putting humans in the loop for anything irreversible. Start with the Docker Compose baseline above, get it running against a low-stakes internal use case, and expand from there once you trust the logs.

  • AI Agents Workflow: Build & Deploy with Docker Compose

    AI Agents Workflow: Building, Containerizing, and Deploying Automated Pipelines

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    If you’ve spent any time building with LLMs, you’ve noticed that a single prompt-response call rarely solves a real problem. Real automation requires an AI agents workflow: a chain of reasoning steps, tool calls, memory lookups, and error handling that runs reliably, unattended, on a schedule or in response to events. This guide walks through designing that workflow, containerizing it so it runs the same way on your laptop and in production, and deploying it on infrastructure that won’t fall over the first time an API call times out.

    We’ll skip the theory-heavy intros you get elsewhere and go straight into architecture, code, and deployment mechanics that a developer or sysadmin can actually use today.

    Why AI Agents Workflow Design Matters for DevOps Teams

    Most teams start with a Jupyter notebook calling an LLM API. That’s fine for a prototype, but it breaks down fast once you need:

  • Retries and backoff when a model provider rate-limits you
  • Persistent state between agent steps (so a crash doesn’t lose progress)
  • Isolation between agents that shouldn’t share credentials or memory
  • Reproducible environments across dev, staging, and production
  • Observability into which step failed and why
  • This is exactly the kind of problem DevOps tooling was built to solve. Treating an AI agent like any other long-running service — something you containerize, schedule, log, and monitor — removes 90% of the flakiness people associate with “AI in production.”

    What Is an AI Agents Workflow, Really?

    Strip away the marketing language and an AI agents workflow is a directed graph of steps:

    1. Trigger — a cron job, webhook, queue message, or file event kicks things off.
    2. Planning — the agent (often an LLM call) decides which tool or sub-agent to invoke next.
    3. Tool execution — actual code runs: a database query, an API call, a shell command, a scrape.
    4. State update — results get written to memory (a vector store, Redis, or plain Postgres row).
    5. Loop or exit — the agent either continues to the next step or returns a final result.

    Frameworks like LangChain and LlamaIndex give you building blocks for steps 2–4, but the trigger, deployment, and reliability layer is still on you. That’s the part this article focuses on.

    Core Components of a Production AI Agents Workflow

    A workflow that survives contact with production needs five pieces, regardless of which agent framework you pick:

  • Orchestrator — decides step order (LangGraph, a custom state machine, or even a simple Python loop)
  • Model client — wraps calls to OpenAI, Anthropic, or a self-hosted model, with retry logic
  • Tool layer — sandboxed functions the agent can call (search, code execution, file I/O)
  • Persistence layer — Redis, Postgres, or SQLite for state and conversation history
  • Runtime — the container and scheduler that actually executes the workflow
  • We’ll build a minimal but real version of each.

    Designing Your First AI Agents Workflow

    Here’s a stripped-down agent loop in Python. It’s intentionally simple so you can see the control flow without framework magic:

    # agent.py
    import os
    import time
    import logging
    from openai import OpenAI
    
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("agent")
    
    client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
    
    def call_model(messages, retries=3):
        for attempt in range(retries):
            try:
                response = client.chat.completions.create(
                    model="gpt-4o-mini",
                    messages=messages,
                    timeout=30,
                )
                return response.choices[0].message.content
            except Exception as e:
                wait = 2 ** attempt
                logger.warning(f"Model call failed ({e}), retrying in {wait}s")
                time.sleep(wait)
        raise RuntimeError("Model call failed after retries")
    
    def run_workflow(task: str) -> str:
        messages = [
            {"role": "system", "content": "You are an ops assistant. Be concise."},
            {"role": "user", "content": task},
        ]
        result = call_model(messages)
        logger.info("Workflow step complete")
        return result
    
    if __name__ == "__main__":
        output = run_workflow("Summarize last night's deployment logs.")
        print(output)

    This is deliberately boring: explicit retries, explicit logging, no hidden control flow. When you move to a framework like LangGraph for multi-step agents, keep that same discipline — every node should log its inputs and outputs so failures are traceable.

    Containerizing the Workflow with Docker

    Once the logic works locally, wrap it in a container so it behaves identically everywhere. Reference Docker’s official documentation if you’re new to multi-stage builds.

    # Dockerfile
    FROM python:3.12-slim AS base
    
    WORKDIR /app
    
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY agent.py .
    
    ENV PYTHONUNBUFFERED=1
    
    CMD ["python", "agent.py"]

    Keep the image slim — python:3.12-slim instead of the full image shaves hundreds of megabytes and reduces attack surface, which matters if this container is talking to external APIs with real credentials.

    Orchestrating Multiple Agents with Docker Compose

    Real workflows usually involve more than one agent plus supporting services (Redis for state, a Postgres database for logs). Docker Compose keeps this manageable without needing a full Kubernetes cluster for a side project or small team deployment.

    # docker-compose.yml
    version: "3.9"
    
    services:
      planner-agent:
        build: ./planner
        environment:
          - OPENAI_API_KEY=${OPENAI_API_KEY}
          - REDIS_URL=redis://redis:6379
        depends_on:
          - redis
        restart: unless-stopped
    
      worker-agent:
        build: ./worker
        environment:
          - REDIS_URL=redis://redis:6379
        depends_on:
          - redis
        restart: unless-stopped
        deploy:
          replicas: 2
    
      redis:
        image: redis:7-alpine
        volumes:
          - redis-data:/data
    
    volumes:
      redis-data:

    The planner-agent decides what work needs doing and pushes tasks onto a Redis queue; worker-agent instances pull tasks and execute tool calls. Running two worker replicas means one slow or stuck agent doesn’t block the whole pipeline — the same pattern you’d use for any queue-based worker system, covered in more depth in our Docker Compose production guide.

    Deploying to Production Infrastructure

    A container that runs locally still needs a home. For most AI agent workloads — which are bursty, I/O-bound on API calls, and don’t need GPU access if you’re calling a hosted model — a modest VPS is enough. A DigitalOcean droplet with 2 vCPUs and 4GB RAM comfortably runs several agent containers plus Redis and Postgres for a small-to-medium workload.

    If you’re running cost-sensitive batch jobs (nightly scraping agents, report generators), Hetzner offers some of the best price-per-core in the market and pairs well with Docker Swarm or Compose deployments that don’t need constant uptime guarantees.

    Deployment steps once you’ve provisioned a host:

    # on the server
    git clone https://github.com/yourorg/agent-workflow.git
    cd agent-workflow
    
    # set secrets
    cp .env.example .env
    nano .env  # add OPENAI_API_KEY, etc.
    
    # bring the stack up
    docker compose up -d --build
    
    # confirm everything is healthy
    docker compose ps
    docker compose logs -f planner-agent

    For anything public-facing (a webhook endpoint that triggers the workflow), put Cloudflare in front of it. Cloudflare’s proxy and WAF rules stop scrapers and abusive traffic from hammering your agent’s trigger endpoint, which matters because every unwanted request is a real API call you’re paying for downstream.

    Monitoring and Observability for AI Agent Pipelines

    Agent workflows fail in unusual ways compared to typical web services — a model might return malformed JSON, hallucinate a tool call that doesn’t exist, or silently loop. Standard uptime monitoring isn’t enough; you need to know when the logic is broken, not just when the container is down.

    A practical monitoring stack:

  • Container/host uptime — a service like BetterStack for status pages and uptime checks on your webhook trigger endpoint
  • Structured logs — ship container logs (docker compose logs) to a central place instead of leaving them on the host
  • Custom metrics — track agent-specific numbers: tool-call failure rate, average steps per task, token spend per run
  • Alerting thresholds — alert if failure rate crosses a threshold (say, more than 10% of runs erroring in a 15-minute window), not just on total outage
  • If you already run Prometheus for other services, exposing a /metrics endpoint from your agent container and scraping it is straightforward — we cover the general pattern in our self-hosted monitoring stack guide.

    Common Pitfalls When Running AI Agents in Production

    A few mistakes show up repeatedly in agent deployments:

  • No timeout on model calls — a hung API call blocks a worker indefinitely; always set an explicit timeout.
  • Unbounded retry loops — retries without a cap can rack up API costs fast when a provider is degraded.
  • Shared credentials across agents — give each agent its own scoped API key so you can revoke and audit independently.
  • No idempotency — if a task gets retried, make sure re-running it doesn’t duplicate side effects (double-sending an email, double-charging a card).
  • Ignoring cost per run — log token usage per task from day one; costs creep up unnoticed otherwise.
  • Running as root in the container — set a non-root user in your Dockerfile; agents that execute arbitrary tool code are a bigger risk surface than typical web apps.
  • Address these before scaling from a single agent to dozens — the failure modes compound quickly once you have more moving parts.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Q: Do I need Kubernetes to run an AI agents workflow in production?
    A: No. Docker Compose on a single well-sized VPS handles most workloads fine. Move to Kubernetes only once you need multi-node scaling or complex autoscaling policies that Compose can’t express.

    Q: How do I handle secrets like API keys in a containerized agent workflow?
    A: Use environment variables injected at runtime via a .env file excluded from version control, or a secrets manager if you’re on a cloud provider. Never bake API keys into the Docker image itself.

    Q: What’s the difference between an agent workflow and a simple script with an LLM call?
    A: A script makes one call and returns. A workflow involves multiple steps, decision points, persistent state, and the ability to recover from partial failure — it behaves like a small service, not a one-off function.

    Q: Should each agent run in its own container?
    A: Generally yes, if agents have different dependencies, scaling needs, or security boundaries. It also makes debugging easier since logs and resource usage are isolated per container.

    Q: How do I keep API costs predictable with autonomous agents?
    A: Cap the maximum number of steps per task, log token usage per run, and set a hard budget alert. Runaway loops are the most common cause of unexpected bills.

    Q: Can I run the LLM itself locally instead of calling a hosted API?
    A: Yes — tools like Ollama let you run open models locally, removing per-call API costs, though you’ll need more CPU/RAM (or a GPU) on your host and should benchmark latency against your workflow’s needs before committing.

    An AI agents workflow is only as reliable as the infrastructure running it. Get the container, orchestration, and monitoring basics right first — the agent logic itself is the easy part once the plumbing doesn’t leak.

  • AI Agent Workflows: Automating DevOps Pipelines Fast

    AI Agent Workflows: A Practical DevOps Guide to Automation

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    AI agent workflows are quickly becoming part of the standard DevOps toolchain. Instead of writing a single script that runs from top to bottom, you’re now composing chains of autonomous or semi-autonomous “agents” that can call tools, read logs, make decisions, and hand off work to other agents. If you run Docker containers, manage a VPS fleet, or maintain CI/CD pipelines, understanding how to design and deploy AI agent workflows is quickly moving from novelty to necessity.

    This guide walks through what AI agent workflows actually are, how to build one from scratch with Python and Docker, how to orchestrate multiple agents reliably, and the operational pitfalls that trip up most first attempts. It assumes you’re comfortable with the command line, Docker, and basic Python — this is written for developers and sysadmins, not marketers.

    What Are AI Agent Workflows, Exactly?

    An AI agent workflow is a pipeline where one or more large language model (LLM)-driven agents perform tasks autonomously, calling external tools (shell commands, APIs, databases) and passing state between steps, with minimal human intervention at each stage. This is different from a simple prompt-response chatbot. A workflow implies:

  • State — the agent remembers what it has already done and what’s left to do.
  • Tool use — the agent can execute code, query a database, hit an API, or run a shell command instead of just generating text.
  • Decision points — the agent (or an orchestrator) decides what to do next based on the result of the previous step.
  • Multi-step execution — the workflow runs across several turns, not a single completion.
  • In a DevOps context, this might mean an agent that watches your deployment logs, decides whether a rollback is needed, opens a ticket, and pings your on-call channel — all without a human writing custom glue code for every possible failure mode.

    Why DevOps Teams Are Adopting AI Agent Workflows

    Traditional automation (cron jobs, Ansible playbooks, shell scripts) is deterministic: you write exact steps for exact conditions. AI agent workflows are useful precisely where conditions are fuzzy — triaging an unfamiliar error message, summarizing a noisy log stream, or deciding which of twenty failing tests actually matters. Teams are adopting agent workflows for:

  • Incident triage — an agent reads alert payloads and recent commits, then drafts a root-cause hypothesis before a human even opens the dashboard.
  • Infrastructure-as-code review — an agent scans a Terraform or Docker Compose diff and flags risky changes (open security groups, removed resource limits) before merge.
  • Log summarization — instead of grepping thousands of lines, an agent condenses them into a two-paragraph summary with the actual error.
  • Automated remediation — for well-understood failure classes (disk full, container OOM-killed), an agent can execute a pre-approved fix and report what it did.
  • None of this replaces monitoring tools like the ones covered in our guide to self-hosted monitoring stacks — agent workflows sit on top of your existing observability data, they don’t replace it.

    Core Building Blocks of an AI Agent Workflow

    Before writing any code, it helps to separate a workflow into its component parts. Every serious agent framework — whether you’re rolling your own or using something like LangChain — breaks down into the same four pieces.

    Agents, Tools, Memory, and Orchestrators

  • Agent — the LLM call itself, wrapped with a system prompt that defines its role and constraints.
  • Tools — functions the agent is allowed to invoke: run_shell_command, query_database, restart_container, send_slack_message. Tools should be narrowly scoped and, where possible, read-only by default.
  • Memory — short-term (the current conversation/task state) and long-term (a vector store or database that persists context across runs).
  • Orchestrator — the piece of code that decides which agent runs next, retries failed steps, and enforces timeouts. This is the part most tutorials skip and the part that actually determines whether your workflow survives production traffic.
  • Getting the tool boundary right matters more than picking the “best” model. An agent with a run_shell_command tool that isn’t sandboxed is a remote code execution vulnerability wearing a trench coat — treat every tool call the same way you’d treat unsanitized user input hitting a shell, because that’s effectively what it is.

    Designing a Simple AI Agent Workflow in Python

    Here’s a minimal single-agent workflow that reads a task, calls a model with a tool definition, and executes the tool result. This is intentionally bare-bones — no framework, just the OpenAI SDK — so you can see exactly what’s happening under the hood before you reach for something heavier.

    import os
    import subprocess
    from openai import OpenAI
    
    client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
    
    ALLOWED_COMMANDS = {"df -h", "uptime", "docker ps", "free -m"}
    
    def run_shell_command(command: str) -> str:
        if command not in ALLOWED_COMMANDS:
            return f"Refused: '{command}' is not on the allowlist."
        result = subprocess.run(command.split(), capture_output=True, text=True, timeout=10)
        return result.stdout or result.stderr
    
    def run_agent_step(task: str, history: list[dict]) -> dict:
        history.append({"role": "user", "content": task})
        response = client.chat.completions.create(
            model="gpt-4.1-mini",
            messages=history,
            tools=[{
                "type": "function",
                "function": {
                    "name": "run_shell_command",
                    "description": "Run a read-only diagnostic command from a fixed allowlist",
                    "parameters": {
                        "type": "object",
                        "properties": {"command": {"type": "string"}},
                        "required": ["command"]
                    }
                }
            }]
        )
        return response.choices[0].message
    
    if __name__ == "__main__":
        history = [{"role": "system", "content": "You are a DevOps diagnostic agent. Use tools to check server health."}]
        msg = run_agent_step("Check disk space and running containers.", history)
        print(msg)

    Notice the ALLOWED_COMMANDS allowlist. This is the single most important line in the file — it’s the difference between a useful diagnostic agent and an agent that will happily run whatever a cleverly-crafted prompt injection tells it to. Never let a tool function pass a raw LLM-generated string straight into subprocess.run(..., shell=True).

    Running AI Agent Workflows in Docker

    Once your agent logic works locally, containerize it. Running agent workflows in Docker gives you process isolation (critical, given the shell-execution risk above), reproducible dependencies, and an easy path to deploying on any VPS. If you haven’t containerized a Python service before, our Docker Compose beginner’s guide covers the basics this section builds on.

    Dockerfile and Compose Setup

    FROM python:3.12-slim
    
    RUN useradd --create-home agent
    WORKDIR /app
    
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY . .
    USER agent
    
    CMD ["python", "agent_runner.py"]

    Running as a non-root user inside the container is not optional here — an agent with shell access that also has root inside a compromised container is a much worse day than one confined to an unprivileged user.

    version: "3.9"
    services:
      agent-orchestrator:
        build: .
        environment:
          - OPENAI_API_KEY=${OPENAI_API_KEY}
          - REDIS_URL=redis://redis:6379/0
        depends_on:
          - redis
        restart: unless-stopped
        deploy:
          resources:
            limits:
              memory: 512M
              cpus: "0.5"
    
      redis:
        image: redis:7-alpine
        volumes:
          - redis-data:/data
    
    volumes:
      redis-data:

    The redis service here isn’t decorative — it’s the queue and short-term memory store that lets multiple agent workers pick up tasks without stepping on each other. Bring the stack up with:

    docker compose up -d --build
    docker compose logs -f agent-orchestrator

    Setting explicit memory and CPU limits matters more for agent workloads than typical web services, since a runaway retry loop calling an LLM API in a tight while True block can burn through both compute and API spend startlingly fast.

    Orchestrating Multi-Agent Pipelines

    Single agents are fine for narrow tasks. Real DevOps workflows usually need multiple specialized agents handing off work: a triage agent that classifies an incident, a investigator agent that pulls logs and metrics, and a remediation agent that executes (or proposes) a fix. Chaining them through a queue rather than direct function calls keeps failures isolated — if the investigator agent times out, the triage classification isn’t lost.

    A simplified Redis-backed handoff looks like this:

    import json
    import redis
    
    r = redis.from_url("redis://redis:6379/0")
    
    def enqueue_task(queue: str, payload: dict) -> None:
        r.rpush(queue, json.dumps(payload))
    
    def dequeue_task(queue: str, timeout: int = 30) -> dict | None:
        item = r.blpop(queue, timeout=timeout)
        return json.loads(item[1]) if item else None
    
    # Triage agent publishes a classified incident
    enqueue_task("investigate", {"incident_id": "INC-1042", "severity": "high"})
    
    # Investigator agent worker loop
    while True:
        task = dequeue_task("investigate")
        if task:
            # run investigator agent, then hand off
            enqueue_task("remediate", {**task, "root_cause": "disk_full"})

    Each agent becomes its own container in the Compose file, scaled independently with docker compose up -d --scale investigator=3. This queue-based pattern is the same pattern used by most production job-processing systems, and it’s far more resilient than chaining agents with direct in-process function calls.

    Monitoring and Reliability for Agent Workflows

    Agent workflows fail differently than normal code — a model can hallucinate a tool call, loop indefinitely retrying the same failed action, or silently drift off-task after several turns. You need visibility into this the same way you’d monitor any other production service. Pair your agent containers with uptime and log monitoring; BetterStack is a solid option if you want incident alerting and log aggregation without standing up your own Grafana/Loki stack.

    A few reliability practices worth baking in from day one:

  • Hard timeouts on every tool call — never let a shell command or API call run indefinitely.
  • Max iteration counts per task — cap agents at, say, 10 turns before forcing a human handoff.
  • Idempotent remediation actions — a remediation agent that restarts a container twice shouldn’t cause worse damage than restarting it once.
  • Structured logging of every tool call and its output — you will need this for debugging and for auditing what an autonomous agent actually did.
  • Cost ceilings — set a per-task token/dollar budget so a stuck loop doesn’t turn into a four-figure API bill.
  • Hosting Considerations

    Agent workloads are bursty — mostly idle, then a flurry of API calls and container spin-ups during an incident. A modest VPS with burstable CPU handles this well; you don’t need a dedicated GPU box since the actual inference happens on the model provider’s infrastructure, not locally. DigitalOcean droplets in the 2-4GB range are typically enough for an orchestrator plus a few worker containers, and their managed Redis/Postgres add-ons remove the need to self-host the queue backend if you’d rather not manage that yourself. If you’re evaluating providers, see our breakdown of VPS options for Docker hosting for a side-by-side comparison of specs and pricing.

    If your agents are exposed via a webhook (for example, receiving alerts from a monitoring tool), put them behind Cloudflare so you get DDoS protection and rate limiting in front of what is, ultimately, code that executes shell commands based on external input. That’s not a place you want raw, unauthenticated exposure.

    Common Pitfalls When Building AI Agent Workflows

    Most failed first attempts at AI agent workflows share the same handful of mistakes:

  • No allowlist on tool inputs — letting the model construct arbitrary shell commands or SQL queries is the fastest way to turn a helpful agent into a security incident.
  • No timeout or retry ceiling — an agent stuck in a reasoning loop will happily call the API a thousand times if nothing stops it.
  • Treating the agent as fully autonomous too early — start with human-in-the-loop approval for any destructive action (deletes, restarts, scaling down), then earn autonomy incrementally as you build confidence in the agent’s judgment.
  • Skipping structured logging — when something goes wrong at 2 a.m., a wall of free-text model output is not a substitute for structured, queryable logs of what tool was called with what arguments.
  • Ignoring cost monitoring — token usage on agent workflows scales with the number of turns, not just the number of tasks, and that’s easy to lose track of.
  • Getting these right up front saves you from re-architecting the whole pipeline after your first production incident caused by the agent itself.

    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    What’s the difference between an AI agent and a regular automation script?
    A regular script follows fixed, deterministic logic you wrote in advance. An AI agent workflow uses an LLM to decide what to do next based on context, calling tools dynamically rather than executing a hardcoded sequence. Agents are better suited to ambiguous, judgment-heavy tasks; scripts are better for anything deterministic and well understood.

    Do I need a framework like LangChain, or can I build agent workflows from scratch?
    For a single agent with one or two tools, a hand-rolled implementation like the one above is often simpler to debug and maintain. Frameworks earn their complexity once you’re coordinating several agents, need built-in memory stores, or want prebuilt integrations — evaluate based on how many moving parts your workflow actually has.

    How do I stop an AI agent from executing dangerous shell commands?
    Use a strict allowlist of permitted commands or actions, never pass raw model output into shell=True execution, run the agent process as a non-root user inside an isolated container, and require human approval for any destructive or irreversible action until you’ve built enough confidence to automate it.

    Can AI agent workflows run entirely on a VPS without a GPU?
    Yes. Unless you’re self-hosting the language model itself, all inference happens via API calls to the model provider. Your VPS only needs enough CPU and RAM to run the orchestrator, tool execution, and any supporting services like Redis or Postgres.

    How much does running an AI agent workflow typically cost?
    Costs scale with the number of model calls (turns) per task, not just the number of tasks completed. A well-bounded workflow with a 5-10 turn cap per task and a smaller model for routine steps keeps costs predictable; always set a hard budget ceiling per task to avoid runaway loops.

    Should agent workflows replace my existing CI/CD and monitoring tools?
    No — they complement them. Agent workflows consume the output of your existing observability and CI/CD systems (logs, metrics, build results) and add a decision-making layer on top. Ripping out deterministic automation in favor of agents for tasks that don’t need judgment is usually a step backward, not forward.

    AI agent workflows are a genuinely useful addition to the DevOps toolbox when scoped correctly: narrow tool access, hard limits on autonomy, and solid monitoring underneath. Start small — a single diagnostic agent with a read-only allowlist — and expand scope only as you build confidence in how it behaves under real, messy production conditions.

  • Zendesk Ai Agent

    Zendesk AI Agent: A Practical Deployment Guide for Support Teams

    Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

    A zendesk ai agent extends a standard Zendesk help desk with automated triage, drafting, and resolution capabilities, letting support teams handle more tickets without proportionally growing headcount. This guide covers how these agents actually work under the hood, how to integrate them with your existing infrastructure, and what to watch for when running one in production.

    What a Zendesk AI Agent Actually Does

    At its core, a zendesk ai agent sits between the customer-facing channel (email, chat widget, help center) and your Zendesk instance, intercepting or augmenting the ticket flow. Depending on configuration, it can:

  • Classify incoming tickets by intent, urgency, and topic
  • Draft or fully send replies to common questions using your knowledge base
  • Route complex tickets to the correct human team with context attached
  • Summarize long ticket threads for agents picking up a handoff
  • Trigger backend actions (refunds, account lookups) through macros or API calls
  • Zendesk’s own AI features (Advanced AI add-on, Answer Bot successor tooling) provide a first-party option, but many teams build or extend a custom zendesk ai agent using the Zendesk API and an external LLM provider, either because they need tighter control over prompts and data flow, or because they want to unify support automation with other internal tools.

    Native vs. Custom Agent Architectures

    Zendesk’s built-in AI agent runs entirely inside Zendesk’s infrastructure and is configured through the admin UI — minimal setup, but limited extensibility. A custom-built alternative runs your own service (often a small Docker container) that authenticates against the Zendesk API, processes tickets with an LLM of your choosing, and writes results back. This second pattern is what the rest of this article focuses on, since it’s the one requiring actual DevOps work.

    Core Architecture of a Self-Hosted Zendesk AI Agent

    A self-hosted zendesk ai agent typically consists of three pieces: an ingestion layer that receives ticket events, a processing layer that calls an LLM, and a write-back layer that posts the result to Zendesk. Zendesk supports both polling the API and receiving webhooks on ticket creation/update, and webhooks are the more efficient choice for anything beyond a low-volume pilot.

    Webhook Ingestion

    Zendesk triggers can call an outbound webhook whenever a ticket matches a condition (e.g., “status is New”). Your zendesk ai agent exposes an HTTP endpoint that receives the ticket payload, which typically includes the ticket ID, subject, description, requester info, and tags. From there, your agent fetches the full conversation via the Zendesk REST API before passing it to an LLM for classification or drafting.

    Containerizing the Agent Service

    Running the agent as a container keeps dependencies isolated and makes deployment repeatable across environments. A minimal Docker Compose setup for a zendesk ai agent service alongside a queue and a database might look like this:

    version: "3.8"
    services:
      zendesk-agent:
        build: .
        restart: unless-stopped
        environment:
          - ZENDESK_SUBDOMAIN=${ZENDESK_SUBDOMAIN}
          - ZENDESK_API_TOKEN=${ZENDESK_API_TOKEN}
          - ZENDESK_EMAIL=${ZENDESK_EMAIL}
          - LLM_API_KEY=${LLM_API_KEY}
        ports:
          - "8080:8080"
        depends_on:
          - redis
      redis:
        image: redis:7-alpine
        restart: unless-stopped
        volumes:
          - redis-data:/data
    volumes:
      redis-data:

    For teams already comparing full-stack setups, the pattern here overlaps closely with a general-purpose Postgres Docker Compose setup if you need persistent ticket-processing history rather than just a Redis queue, and the same Docker Compose secrets management guide applies directly to keeping ZENDESK_API_TOKEN and your LLM key out of plain environment files.

    Authenticating a Zendesk AI Agent Against the API

    Zendesk supports API token authentication and OAuth. For a service-to-service integration like a zendesk ai agent, an API token tied to a dedicated agent user (not a personal account) is the simplest and most maintainable option. Store the token as a secret, never in source control, and scope the associated agent user’s permissions as narrowly as Zendesk’s role system allows — most automation agents only need read/write access to tickets, not full admin rights.

    Rate Limits and Retry Handling

    Zendesk enforces per-endpoint rate limits, and a zendesk ai agent processing tickets in bulk (e.g., backfilling a queue after downtime) will hit 429 responses if it doesn’t respect the Retry-After header. Build exponential backoff into your API client from day one rather than retrofitting it after a production incident — a burst of retries during a rate-limit window can itself make the throttling worse.

    curl -s -o /dev/null -w "%{http_code}n" 
      -u "[email protected]/token:$ZENDESK_API_TOKEN" 
      "https://yoursubdomain.zendesk.com/api/v2/tickets.json?page[size]=1"

    Use this kind of quick check during deployment to confirm credentials and connectivity before wiring the full agent pipeline in.

    Prompt Design and Response Quality for Zendesk AI Agents

    The quality of a zendesk ai agent’s output depends heavily on what context it’s given, not just the underlying model. Feed the agent the full ticket thread, relevant help center articles, and prior resolution notes for similar tickets when available. Avoid asking the model to draft a reply from the subject line alone — this is the most common cause of generic, unhelpful auto-responses that erode customer trust.

    Guardrails Before Auto-Sending

    Most teams start a zendesk ai agent in “draft” mode, where it writes a suggested reply into a private note or draft comment for a human agent to review and send. Only after measuring accuracy over a meaningful sample of tickets should auto-send be enabled, and even then, typically restricted to a narrow set of well-understood intents (password resets, order status, documented FAQ topics) rather than the full inbox.

    A simple guardrail list worth enforcing in code, not just prompt instructions:

  • Never auto-send a reply that mentions a refund or account change without human confirmation
  • Escalate immediately on detected negative sentiment plus an account-cancellation keyword
  • Cap auto-resolution to intents with a confirmed historical accuracy above your team’s chosen threshold
  • Log every auto-sent reply with the input context for later audit
  • Monitoring and Operating a Zendesk AI Agent in Production

    Once live, a zendesk ai agent needs the same operational rigor as any other production service. Track latency (time from webhook receipt to reply drafted), error rates against the Zendesk API, and LLM provider errors separately, since they usually require different remediation. If the agent runs in Docker, docker compose logs on the service is the first place to look during an incident — see the Docker Compose logs debugging guide for a structured approach to filtering and following logs under load.

    Handling Agent Restarts Without Losing In-Flight Tickets

    Because webhook deliveries aren’t guaranteed to be replayed if your agent is down at the moment Zendesk sends them, pair the webhook with a periodic reconciliation job that polls for tickets updated in the last N minutes and checks whether they were actually processed. This closes the gap left by any missed webhook delivery. If you need to rebuild the container after a dependency change, the Docker Compose rebuild guide covers the difference between a plain restart and a full rebuild, which matters if your agent image needs to pick up new code.

    Extending a Zendesk AI Agent With Workflow Automation

    Rather than building every integration by hand, many teams route Zendesk events through a workflow automation tool like n8n, which can call the Zendesk API, transform ticket data, and forward it to an LLM node or external agent service without custom glue code for every connector. This is a reasonable middle ground between the fully native Zendesk AI add-on and a fully custom service. If you’re evaluating this route, the n8n self-hosted installation guide walks through getting a Docker-based n8n instance running, and How to Build AI Agents With n8n covers the agent-node pattern specifically, which maps well onto ticket-classification and reply-drafting use cases. Teams also considering broader automation vendors sometimes compare n8n directly against alternatives — see the n8n vs Make comparison if that decision is still open.

    For infrastructure hosting the agent itself, a small VPS is usually sufficient for low-to-moderate ticket volume, since the actual LLM inference happens against an external API rather than locally. Providers like DigitalOcean or Hetzner offer VPS tiers that comfortably run a webhook listener, a queue, and a small database for this workload.

    Security Considerations for a Zendesk AI Agent

    A zendesk ai agent has access to customer conversations, which may include personal data, account details, and sometimes payment-adjacent information. Treat the webhook endpoint as a public attack surface: verify Zendesk’s webhook signing secret on every incoming request, reject unsigned or mismatched payloads outright, and run the endpoint behind TLS. Rotate the Zendesk API token periodically and immediately if it’s ever exposed in a log or committed accidentally. If the agent forwards ticket content to an external LLM provider, confirm that provider’s data retention policy is compatible with your company’s privacy commitments before sending real customer data through it — this is a contractual and compliance question as much as a technical one, and worth resolving before go-live rather than after.


    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Does a zendesk ai agent replace human support agents entirely?
    No. Most production deployments use a zendesk ai agent to handle a subset of well-understood, repetitive tickets (password resets, order status, FAQ-style questions) while routing everything else — and anything low-confidence — to a human agent. Full replacement for general support is not a realistic near-term goal for most teams.

    Can I build a zendesk ai agent without Zendesk’s paid AI add-on?
    Yes. Zendesk’s REST API and webhook triggers are available on standard plans, so you can build a custom zendesk ai agent using any LLM provider without purchasing Zendesk’s native AI add-on, though you lose the native UI configuration and have to build that tooling yourself.

    How do I test a zendesk ai agent before enabling auto-send?
    Run it in draft mode first, where it writes suggested replies as internal notes rather than sending them, and have human agents review and rate accuracy over a representative sample of real tickets before considering any auto-send behavior.

    What happens if the LLM provider is down when a ticket comes in?
    Design the agent to fail gracefully: if the LLM call errors or times out, the ticket should fall back to normal human routing rather than being dropped or stuck in a retry loop. Alerting on LLM provider errors separately from Zendesk API errors makes this failure mode easy to spot.

    Conclusion

    A zendesk ai agent can meaningfully reduce the manual load on a support team, but the value comes from careful integration work — reliable webhook handling, sensible guardrails before auto-sending anything, and production-grade monitoring — not from the LLM alone. Whether you build a custom agent on your own infrastructure or extend Zendesk’s native AI tooling, start conservatively in draft mode, measure real accuracy on your own ticket volume, and expand scope only as confidence in the agent’s output grows. For deeper reference on the underlying API and container tooling used throughout this guide, see Zendesk’s developer documentation and Docker’s official Compose documentation.