How to Create an AI Agent with Docker and Python

How to Create an AI Agent: A Developer’s Step-by-Step 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 framework, tutorial, and vendor pitch these days claims to help you “create an AI agent” in five minutes. Most of them skip the part that actually matters to developers and sysadmins: how do you build something reliable, containerize it, and run it in production without it falling over or leaking your API keys? This guide walks through the full lifecycle — architecture, code, Docker packaging, and deployment — using tools you already know.

We’ll build a small but functional AI agent in Python, wrap it in Docker, and deploy it to a VPS with proper logging and monitoring. No hand-wavy diagrams, just working code you can copy and run.

What Is an AI Agent?

An AI agent is a program that uses a language model to decide what to do next, not just what to say. Unlike a chatbot that returns text, an agent can call tools, read files, hit APIs, and loop until a goal is satisfied. The distinction matters because it changes how you architect and deploy the thing.

Agents vs. Chatbots vs. Scripts

  • A script runs a fixed sequence of steps you defined in advance.
  • A chatbot takes input and returns text — no autonomous action.
  • An agent observes state, reasons about a goal, chooses a tool, executes it, and repeats until done.
  • That loop — observe, reason, act, repeat — is the entire mental model. Everything else (frameworks like LangChain, vector databases, memory stores) is scaffolding around that loop.

    Core Components Every AI Agent Needs

    Before writing code, you need four pieces in place:

  • A model or API to do the reasoning (OpenAI, Anthropic, or a self-hosted model via Ollama)
  • A tool interface so the agent can take real actions (shell commands, HTTP calls, file I/O)
  • A loop controller that manages iterations, retries, and stop conditions
  • Logging and guardrails so the agent doesn’t run unbounded API calls or destructive commands
  • Skipping the guardrails is the single most common mistake we see in production incidents — an agent stuck in a retry loop can burn through an API budget or hammer a downstream service in minutes.

    Setting Up Your Development Environment

    You’ll need Python 3.11+, pip, and an API key from your model provider of choice. If you’re running this on a fresh Linux box, our Docker Compose guide for beginners covers getting a clean environment up quickly.

    Installing Python and Dependencies

    # Create an isolated environment
    python3 -m venv agent-env
    source agent-env/bin/activate
    
    # Install core dependencies
    pip install openai python-dotenv requests

    Store your API key in a .env file rather than hardcoding it:

    echo "OPENAI_API_KEY=sk-your-key-here" > .env
    echo ".env" >> .gitignore

    Never commit API keys to version control. If you’re new to secrets management, the OpenAI API documentation has a good primer on key rotation and scoping.

    Building Your First AI Agent in Python

    Here’s a minimal but real agent loop. It takes a goal, decides whether to call a tool or respond directly, executes the tool, and feeds the result back until the model signals it’s done.

    # agent.py
    import os
    import json
    from dotenv import load_dotenv
    from openai import OpenAI
    
    load_dotenv()
    client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
    
    def get_weather(city: str) -> str:
        # Placeholder for a real API call
        return f"It is 72F and clear in {city}."
    
    TOOLS = {
        "get_weather": get_weather,
    }
    
    TOOL_SCHEMA = [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Get current weather for a city",
                "parameters": {
                    "type": "object",
                    "properties": {"city": {"type": "string"}},
                    "required": ["city"],
                },
            },
        }
    ]
    
    def run_agent(goal: str, max_iterations: int = 5) -> str:
        messages = [{"role": "user", "content": goal}]
    
        for i in range(max_iterations):
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=messages,
                tools=TOOL_SCHEMA,
            )
            choice = response.choices[0]
            messages.append(choice.message)
    
            if not choice.message.tool_calls:
                return choice.message.content
    
            for call in choice.message.tool_calls:
                fn_name = call.function.name
                args = json.loads(call.function.arguments)
                result = TOOLS[fn_name](**args)
                messages.append({
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": result,
                })
    
        return "Max iterations reached without a final answer."
    
    if __name__ == "__main__":
        print(run_agent("What's the weather like in Austin?"))

    That max_iterations cap is the guardrail mentioned earlier. Without it, a malformed tool response can send the agent into an infinite loop that quietly drains your API quota.

    Giving Your Agent Tools

    Real agents need more than a weather stub. Common tools include shell execution, file reads, and HTTP requests. Wrap each one defensively:

    import subprocess
    
    def run_shell(command: str) -> str:
        # Restrict to an allowlist in production — never pass raw LLM output to shell=True
        allowed = {"ls", "df", "uptime"}
        cmd = command.split()[0]
        if cmd not in allowed:
            return f"Command '{cmd}' is not permitted."
        result = subprocess.run(command.split(), capture_output=True, text=True, timeout=10)
        return result.stdout or result.stderr

    The allowlist isn’t optional. An agent with unrestricted shell access is a remote code execution vulnerability with extra steps.

    Containerizing Your AI Agent with Docker

    Once the agent runs locally, package it so it behaves identically everywhere — your laptop, CI, and production VPS.

    Writing the Dockerfile

    FROM python:3.11-slim
    
    WORKDIR /app
    
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    COPY agent.py .
    
    # Run as a non-root user
    RUN useradd -m agentuser
    USER agentuser
    
    CMD ["python", "agent.py"]

    Running as a non-root user inside the container limits the blast radius if the agent’s shell tool is ever tricked into executing something unexpected.

    docker-compose for Local Testing

    version: "3.9"
    services:
      ai-agent:
        build: .
        env_file: .env
        restart: unless-stopped
        deploy:
          resources:
            limits:
              memory: 512M
              cpus: "0.5"
        logging:
          driver: json-file
          options:
            max-size: "10m"
            max-file: "3"

    Build and run it:

    docker compose up --build

    The memory and CPU limits matter more for agents than typical web services — a runaway reasoning loop can spike CPU usage in a way a stateless API rarely does.

    Deploying Your AI Agent to a VPS

    For anything beyond local testing, you need a real server. A $6–12/month VPS is plenty for most single-agent workloads; you don’t need Kubernetes for this.

    Setting Up a Production-Ready VPS

    A solid starting spec looks like:

  • 2 vCPUs, 2–4GB RAM (agents are memory-light unless you’re running a local model)
  • Ubuntu 22.04 LTS or Debian 12
  • Docker and Docker Compose installed
  • A non-root deploy user with SSH key auth only
  • UFW or a cloud firewall restricting inbound ports to SSH and whatever port your agent exposes
  • Both DigitalOcean and Hetzner offer droplets/instances in this price range with one-click Docker images, which cuts your setup time down to a few minutes. If you’re comparing providers, our best VPS providers for self-hosting breakdown covers pricing and network performance in more depth.

    Once your server is ready:

    # On the VPS
    git clone https://github.com/yourname/ai-agent.git
    cd ai-agent
    cp .env.example .env   # fill in real keys
    docker compose up -d --build

    Check it’s running:

    docker compose logs -f ai-agent

    Monitoring, Logging, and Securing Your Agent

    An agent that fails silently is worse than one that fails loudly. At minimum, ship logs somewhere durable and set up uptime alerts.

  • Send container logs to a centralized log drain instead of relying on docker logs on a box you might reboot
  • Set up uptime and error-rate monitoring with a service like BetterStack so you get paged before a customer notices
  • If your agent exposes an HTTP endpoint, put it behind Cloudflare for DDoS protection, rate limiting, and TLS termination rather than exposing it raw
  • Rotate API keys regularly and scope them to the minimum permissions the agent actually needs
  • Cap token usage and dollar spend per run — most model providers let you set hard usage limits per API key
  • These aren’t nice-to-haves. An unmonitored agent with an unbounded loop and a live API key is one bad prompt away from a surprise bill.

    Handling Failures Gracefully

    Wrap your agent’s main loop in retry logic with exponential backoff for transient API failures, and make sure a crash restarts the container instead of leaving it dead:

        restart: unless-stopped

    That one line in your compose file has saved more on-call pages than any amount of clever error handling.

    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 framework like LangChain to create an AI agent?
    No. For a single-purpose agent, plain Python with the model provider’s SDK (as shown above) is easier to debug and has fewer dependencies. Frameworks earn their keep once you’re managing multiple agents, complex memory, or many tool integrations.

    Can I run an AI agent without paying for a cloud model API?
    Yes — tools like Ollama let you run open models like Llama or Mistral locally or on your own VPS, trading API cost for compute cost and slightly lower reasoning quality.

    How do I stop an agent from running away with API costs?
    Set a hard max_iterations cap in your loop, enforce per-key spend limits with your model provider, and monitor usage daily until you trust the agent’s behavior in production.

    Is Docker actually necessary for a simple agent script?
    Not strictly, but it guarantees the same Python version, dependencies, and OS libraries wherever you deploy, which eliminates an entire class of “works on my machine” bugs.

    How much does it cost to run an AI agent in production?
    Expect $6–15/month for the VPS, plus variable API costs that depend entirely on call volume and model choice — a lightweight agent making a few dozen calls a day can cost under $5/month in API fees.

    What’s the biggest security risk with AI agents?
    Giving the agent tools with more access than it needs — especially unrestricted shell or file system access. Always allowlist commands and scope credentials tightly.

    Creating an AI agent isn’t magic — it’s a loop, a couple of tools, and the same deployment discipline you’d apply to any other service. Start small, cap your iterations, containerize early, and monitor from day one.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *