DEV Community

John Wick
John Wick

Posted on

Deployment of Python Bot on Koyeb and Heroku using StayPresent

Deploying Python Bots to Koyeb and Heroku with StayPresent

Render and Railway tend to dominate the conversation around free Python bot hosting, but Koyeb and Heroku are both still widely used, and both come with their own quirks around ports, process types, and health checks. This guide walks through a koyeb heroku python bot deployment using StayPresent, covering what's different about each platform and what stays exactly the same.

Table of Contents

  1. What Koyeb and Heroku Have in Common
  2. Deploying to Heroku with a Procfile
  3. Heroku Dyno Sleeping
  4. Deploying to Koyeb
  5. Koyeb Health Checks
  6. Shared StayPresent Configuration
  7. Full Example for Both Platforms
  8. Best Practices
  9. Common Mistakes
  10. FAQs
  11. Conclusion

What Koyeb and Heroku Have in Common

Despite different dashboards and deployment flows, both platforms share the same core expectation that trips up most bot deployments: a web-facing process needs to bind to a port the platform assigns, typically via a PORT environment variable, and something needs to respond to health checks on that port. Neither platform inherently knows or cares what your bot is doing internally — Discord gateway connections, Telegram polling, whatever — it only cares whether the process it started is listening where expected.

Deploying to Heroku with a Procfile

Heroku uses a Procfile to declare how to start your app:

web: python main.py
Enter fullscreen mode Exit fullscreen mode

main.py reads Heroku's assigned port the same way it would on Render or Railway:

import os
import staypresent

staypresent.web.json({"status": "running"})

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
)
Enter fullscreen mode Exit fullscreen mode

Heroku requires the process type to be web (not worker) for the platform to route external HTTP traffic to it, which matters if you're relying on Heroku's own health checks or want the app reachable at its public URL for staypresent.cron().

Heroku Dyno Sleeping

Free and low-cost Heroku dynos have historically been subject to inactivity sleeping, similar to Render's free tier. The fix is the same self-ping pattern:

staypresent.cron(
    "https://my-app.herokuapp.com",
    interval=240,
)
Enter fullscreen mode Exit fullscreen mode

As with any platform, this must target the public herokuapp.com URL, never 127.0.0.1 or 0.0.0.0, since only external traffic resets an inactivity timer.

Deploying to Koyeb

Koyeb deploys directly from a Git repository or a Dockerfile, and — like Heroku and Render — injects a PORT environment variable that your service must bind to:

import os
import staypresent

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
)
Enter fullscreen mode Exit fullscreen mode

If you're deploying via Dockerfile on Koyeb, the same container setup described for VPS/Docker deployments applies unchanged — Koyeb just runs the container for you.

Koyeb Health Checks

Koyeb performs HTTP health checks against a configurable path. Point it at StayPresent's built-in /health route:

staypresent.web.json({"status": "running"})
# /health is provisioned automatically and returns {"status": "ok"}
Enter fullscreen mode Exit fullscreen mode

This keeps Koyeb's platform-level health check independent from whatever custom status payload you're serving at /.

Shared StayPresent Configuration

Because the underlying requirement — a bound port, a health check, crash-resilient process management — is identical across Koyeb, Heroku, Render, and Railway, the exact same main.py typically works across all four with zero platform-specific branching:

import os
import staypresent

staypresent.web.json({"status": "running"})

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
    restart_on_crash=True,
    max_restarts=5,
)
Enter fullscreen mode Exit fullscreen mode

Full Example for Both Platforms

Heroku (Procfile):

web: python main.py
Enter fullscreen mode Exit fullscreen mode

Koyeb (Dockerfile-based):

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
Enter fullscreen mode Exit fullscreen mode

Both point at the same entry point:

import os
import staypresent

staypresent.web.json({"status": "running"})
staypresent.cron(f"https://{os.getenv('APP_URL', '')}", interval=240)

staypresent.run(
    "bot.py",
    port=int(os.getenv("PORT", 8080)),
    restart_on_crash=True,
    max_restarts=5,
)
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Set the Heroku process type to web, not worker, if you need the app reachable at a public URL for health checks or self-ping.
  • On Koyeb, configure the health check path explicitly to /health in the service settings rather than relying on the default root path.
  • Keep platform-specific values (like the public URL used for cron()) in an environment variable rather than hardcoding them, since the URL differs between platforms and between staging/production.

Common Mistakes

  • Declaring a worker dyno on Heroku for a bot that also needs a keep-alive endpoint. Worker dynos aren't reachable via HTTP the way web dynos are, which defeats the purpose of running StayPresent's server at all.
  • Assuming Koyeb and Heroku behave identically for free-tier sleeping. Policies change over time on both platforms — check current documentation rather than assuming past behavior still applies.
  • Not setting APP_URL or equivalent, leaving cron() pointed at an empty string instead of the actual public domain.

FAQs

Can I use the exact same codebase across Render, Railway, Koyeb, and Heroku?
Yes — as long as you read PORT from the environment and target the correct public URL for self-ping, the StayPresent configuration doesn't need to change per platform.

Does Koyeb require Docker?
No, Koyeb also supports direct Git-based buildpack deployments, in which case you don't need a Dockerfile at all — just the same main.py entry point.

Do free tiers on these platforms still exist?
Availability and limits on free tiers change over time on every platform — always check current pricing pages before committing to a deployment plan.

Conclusion

A koyeb heroku python bot deployment doesn't require separate code paths for each platform. Bind to the assigned PORT, expose StayPresent's /health endpoint for platform health checks, and optionally self-ping the public URL — the same main.py carries across Koyeb, Heroku, Render, and Railway without modification.

pip install staypresent[prod]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)