DEV Community

Chudi Nnorukam
Chudi Nnorukam

Posted on • Originally published at chudi.dev

Python Agents on DigitalOcean: Deploy in 5 Steps

Originally published at chudi.dev


Disclosure: This post contains affiliate links to DigitalOcean. If you sign up through these links, I earn a commission at no extra cost to you, and you get $200 in free credits for 60 days. I ran my trading bot on a DigitalOcean Droplet before migrating to a specialized VPS for lower latency. I recommend DO for Python agents because I used it and it worked.


My Python trading bot worked perfectly on my laptop. Asyncio event loop, WebSocket connections to Binance, real-time order placement on Polymarket. Clean code. Passed review. Ran great locally.

Then I tried to deploy it.

The first attempt was AWS Lambda. Cold starts added 400ms to every signal. The 15-minute timeout killed my long-running WebSocket connections. I spent two days fighting CloudWatch logs before I realized: Lambda was built for HTTP request handlers, not for a process that needs to stay alive.

Here's what I wish someone had told me: deploying a Python agent that runs 24/7 costs $6/month and takes 30 minutes. Not $50. Not $80. Six dollars.

I ran my Polymarket trading bot on a $6 DigitalOcean Droplet for months. It processed live Binance price feeds, placed orders on the CLOB, and managed exits autonomously. 69.6% win rate across 23 clean trades. The infrastructure never failed me. The server was not the bottleneck. It never was.

This is the setup guide that would have saved me those two days on Lambda.

What does a Python agent actually need?

Most developers pick their deployment platform based on what they already know. If you've used AWS before, you reach for EC2 or Lambda. If you're a Heroku person, you spin up a dyno. Nobody stops to ask: what are the actual infrastructure requirements?

A long-running Python agent requires:

  • A process that stays alive (not a function that runs and dies)
  • Persistent connections (WebSocket feeds, database connections)
  • Predictable cost (not pay-per-invocation that spikes when your bot gets active)
  • Full control over the runtime (Python version, system packages, cron)

Here's what it does not need:

  • Auto-scaling (your bot is one process)
  • Load balancers (it's not serving HTTP traffic)
  • Managed runtimes (you want control, not guardrails)
  • A $50/month bill for resources you'll never use

That last point stings. If you're running a single Python agent on Heroku ($7-25/mo), Railway ($5 + metered usage), or an EC2 instance you forgot to right-size ($15-80/mo depending on how lost you got in the AWS console), you're paying for infrastructure designed for problems you don't have.

How do DigitalOcean costs compare to AWS, Heroku, and Railway?

I evaluated four providers before my first deploy. Here's the honest comparison:

Provider Monthly Cost Best For The Catch
AWS EC2 $8-80/mo You already live in AWS Console is a maze. Surprise bills are real. A t3.micro costs $8, but you'll add EBS, bandwidth, and by month 3 you're at $35 wondering what happened.
Heroku $7-25/mo Quick web app deploys Dynos sleep on the free tier. Paid tier starts at $7 but a worker dyno for a bot is $25. No SSH access. Limited debugging.
Railway $5 + usage Git-push simplicity Usage-based pricing sounds cheap until your bot runs 24/7. A busy month can cost $20-40.
DigitalOcean $6/mo flat Bots, agents, anything that runs 24/7 Fewer regions than AWS. You manage your own server. That's it.

I picked DigitalOcean. $6/month flat. No metered surprises. Full root access. The documentation read like it was written by someone who actually uses the product. I went from zero to running bot in 28 minutes.

Here's what that $6 gets you: 1 vCPU, 1GB RAM, 25GB SSD, 1TB transfer. My trading bot (asyncio event loop, multiple WebSocket connections, real-time order placement) used about 200MB of that RAM. The CPU barely touched 5% between trading signals.

Most of you are overpaying. Let me show you the setup.

What do you need before starting?

You need three things: Python 3.10 or higher on your local machine, an SSH key pair (generate one with ssh-keygen -t ed25519 if you don't have one), and 30 minutes of uninterrupted time.

That's literally all the prerequisites.

Step 1: Create a Droplet (2 Minutes)

Log into DigitalOcean and create a new Droplet:

  • Region: Pick the closest to your data source. For my trading bot, I chose Amsterdam (5-12ms to Polymarket's CLOB in London). For a general agent, pick the region nearest whatever API you call most.
  • Image: Ubuntu 24.04 LTS
  • Size: Basic, Regular, $6/month (1 vCPU, 1GB RAM, 25GB SSD)
  • Authentication: SSH Key (paste your public key from ~/.ssh/id_ed25519.pub)

Never choose password authentication. SSH keys only. This is non-negotiable. I'll explain why in Step 2.

The Droplet spins up in about 60 seconds. Grab the IP address from the dashboard. Test your connection:

ssh root@YOUR_DROPLET_IP
Enter fullscreen mode Exit fullscreen mode

That root prompt means you have a server. Running. Waiting for your code. For $6/month, you now own a machine that will run 24/7 whether or not you're watching.

Step 2: Lock It Down (10 Minutes)

I skipped this step the first time. A week later, I checked the auth log and found 3,000 brute-force SSH attempts from IPs I'd never seen. Nothing was compromised (SSH keys are strong), but it was a wake-up call.

Ten minutes of security setup prevents real problems:

# Create a deploy user (never run your bot as root)
adduser --disabled-password agent
usermod -aG sudo agent

# Copy your SSH key to the new user
mkdir -p /home/agent/.ssh
cp /root/.ssh/authorized_keys /home/agent/.ssh/
chown -R agent:agent /home/agent/.ssh
chmod 700 /home/agent/.ssh
chmod 600 /home/agent/.ssh/authorized_keys

# Disable root login and password auth
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# Enable the firewall
ufw allow OpenSSH
ufw --force enable
Enter fullscreen mode Exit fullscreen mode

Log out. Reconnect as the agent user:

ssh agent@YOUR_DROPLET_IP
Enter fullscreen mode Exit fullscreen mode

You now have a locked-down server. Root login disabled, password auth disabled, firewall active. This is what separates a production server from a tutorial project.

Step 3: Set Up Python (3 Minutes)

Ubuntu 24.04 ships with Python 3.12. Set up a virtual environment:

sudo apt update && sudo apt install -y python3-venv python3-pip

mkdir -p ~/my-agent
cd ~/my-agent

python3 -m venv venv
source venv/bin/activate

pip install aiohttp websockets python-dotenv
Enter fullscreen mode Exit fullscreen mode

Always use a venv. I learned this the hard way when a system-level pip install broke apt's Python dependencies on my first Droplet. Took me an hour to untangle. A venv takes 10 seconds to create and prevents that entirely.

Step 4: Deploy Your Agent (5 Minutes)

Here's a production-ready async agent skeleton. This is the same pattern my trading bot used. Replace the inner logic with whatever your agent does:

# ~/my-agent/agent.py
import asyncio
import signal
import logging
from datetime import datetime

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("agent.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

shutdown_event = asyncio.Event()

def handle_signal(sig, frame):
    logger.info(f"Received signal {sig}, shutting down gracefully...")
    shutdown_event.set()

async def process_tick(data: dict):
    """Your agent logic goes here."""
    logger.info(f"Processing: {data}")

async def run_agent():
    """Main agent loop. Replace with your real logic."""
    logger.info("Agent started")

    cycle = 0
    while not shutdown_event.is_set():
        try:
            await process_tick({"cycle": cycle, "ts": datetime.utcnow().isoformat()})
            cycle += 1
            await asyncio.sleep(10)

        except Exception as e:
            logger.error(f"Agent error: {e}")
            await asyncio.sleep(5)

    logger.info("Agent stopped cleanly")

def main():
    signal.signal(signal.SIGTERM, handle_signal)
    signal.signal(signal.SIGINT, handle_signal)
    asyncio.run(run_agent())

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Create a .env file for configuration:

# ~/my-agent/.env
API_KEY=your_api_key_here
POLL_INTERVAL=10
LOG_LEVEL=INFO
Enter fullscreen mode Exit fullscreen mode

Test it on the Droplet:

cd ~/my-agent
source venv/bin/activate
python3 agent.py
Enter fullscreen mode Exit fullscreen mode

You should see log output. Press Ctrl+C to stop. If it runs for 30 seconds clean, you're ready for the part most tutorials skip.

How do you keep a Python agent running 24/7 on a Droplet?

Use systemd. This is the critical step separating "I deployed my bot" from "my bot runs in production."

Running your agent in screen or tmux kills it when SSH disconnects, server reboots, or when your code crashes at 3am with nobody watching. I lost 6 hours of trading signals before learning about systemd.

systemd handles three essential tasks:

  1. Automatically restarts your agent if it crashes
  2. Starts it on server reboot without manual intervention
  3. Manages logs for debugging and audit trails

Create a service file:

sudo tee /etc/systemd/system/my-agent.service << 'EOF'
[Unit]
Description=My Python Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=agent
WorkingDirectory=/home/agent/my-agent
Environment=PATH=/home/agent/my-agent/venv/bin:/usr/bin
EnvironmentFile=/home/agent/my-agent/.env
ExecStart=/home/agent/my-agent/venv/bin/python3 agent.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF
Enter fullscreen mode Exit fullscreen mode

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable my-agent
sudo systemctl start my-agent
Enter fullscreen mode Exit fullscreen mode

Check it:

sudo systemctl status my-agent
Enter fullscreen mode Exit fullscreen mode

active (running). Your agent now survives reboots, crashes, and SSH disconnects. Close your laptop. Go to sleep. It keeps running.

Step 6: What are the common mistakes after deployment?

I hit all of these. You'll hit at least two.

1. "ModuleNotFoundError" after deploy

systemd runs its own environment. If ExecStart points to python3 instead of /home/agent/my-agent/venv/bin/python3, it uses the system Python which doesn't have your packages. Exact path. Every time.

2. Agent dies silently after 6 hours

An unhandled exception in the main loop. Without the try/except wrapper, the agent crashes, systemd restarts it, it crashes again on the same data, and you hit the restart rate limit. The backoff sleep is not optional.

3. Disk fills up from logs

journalctl handles systemd logs, but your agent.log file grows without bounds. I discovered this after a 2GB log file ate my 25GB disk. Add log rotation:

sudo tee /etc/logrotate.d/my-agent << 'EOF'
/home/agent/my-agent/agent.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}
EOF
Enter fullscreen mode Exit fullscreen mode

4. SSH disconnect kills the agent

Only happens if you started with python3 agent.py & in a shell session. systemd doesn't have this problem. If you're still running bots in tmux: stop. Use systemd.

How do you deploy code changes to your running agent?

When you update your agent code:

# From your local machine
scp agent.py agent@YOUR_DROPLET_IP:~/my-agent/

# Restart the service
ssh agent@YOUR_DROPLET_IP "sudo systemctl restart my-agent"

# Verify clean start
ssh agent@YOUR_DROPLET_IP "sudo journalctl -u my-agent -n 10 --no-pager"
Enter fullscreen mode Exit fullscreen mode

Three commands. Under 10 seconds. I started with scp and switched to git-based deploys after the third time I forgot to push a dependency file. For a single agent, scp is fine.

What should you verify before going live?

Before you trust your agent with real work or money:

  • [ ] SSH key auth only (password auth disabled)
  • [ ] Firewall active (ufw status shows SSH allowed, everything else denied)
  • [ ] Non-root user running the agent
  • [ ] systemd service with Restart=always
  • [ ] Error handling in the main loop (catch, log, backoff, continue)
  • [ ] Signal handlers for SIGTERM/SIGINT
  • [ ] Log rotation configured
  • [ ] Monitoring (check logs daily until stable)

If your agent handles money, also add: position limits, stop-losses, health check endpoints, and alert notifications. I documented the full trading bot architecture, risk management, and signal detection patterns in my Polymarket trading bot guide.

When do you outgrow a $6 Droplet?

I migrated away from DigitalOcean when my trading strategy demanded sub-10ms round-trip latency to Polymarket's CLOB. The Amsterdam region provided 5-12ms, which worked for my initial strategy. When I needed 3-5ms consistency, I switched to a specialized VPS provider colocated near the exchange.

That migration took 4 months. It only mattered because I was doing latency arbitrage where 100ms cost me money.

For most Python agents, you will not outgrow a $6 Droplet for years.

You'll know it's time to upgrade when:

  • Milliseconds matter: Your agent's profitability depends on execution speed
  • Multiple agents: You're running 4+ processes and hitting CPU/RAM limits
  • GPU inference: Your agent runs ML models that need a GPU
  • Compliance: You need certifications or regions DO doesn't offer

Until then, every month you spend more than $6 on infrastructure for a single Python agent is money you didn't need to spend.

Start Here

If you've been running your bot on Lambda hitting execution timeouts, paying $25/month for a Heroku worker dyno, or avoiding deployment because AWS console complexity paralyzes you:

  1. Sign up for DigitalOcean (claim $200 in free credits for 60 days, enough to test this entire setup cost-free)
  2. Follow the 6-step process above (30 minutes total, no dependencies)
  3. Replace the example agent skeleton with your actual logic
  4. Run through the production checklist and monitor for 48 hours

Result: $6/month. 30 minutes setup time. Your agent running 24/7 on infrastructure you control completely.

For trading bot builders: Start with my guide on building a Polymarket trading bot to understand signal detection, order placement, risk management, and profitability metrics. Then return here for the deployment infrastructure. My bot achieved 69.6% win rate on this exact setup before migrating for latency reasons.

For general AI agents: This Droplet setup works equally well for data crawlers, scheduled scrapers, webhook processors, LLM orchestration systems, and autonomous agents. The pattern applies to any Python process that needs 24/7 uptime without paying cloud premium prices.

What are you deploying? I'm curious what agents people are running on VPS these days.

Sources

Top comments (0)