Running a bot 24/7, in practice
What 24/7 actually asks of a machine
A bot on your laptop is not a service, it is a session. The lid closes and the network adapter sleeps. Windows installs an update at three in the morning and reboots. You close the terminal window and the shell sends SIGHUP to everything it started. Your home connection renegotiates and the socket dies. None of these are unusual events, and each one takes the bot offline while you are asleep and not watching.
A platform that suspends processes which have not received an inbound HTTP request for a while fails in a more specific way, because that assumption does not fit a bot at all. A Discord bot holds a persistent outbound WebSocket to the gateway: it receives constantly and is asked for nothing. To an idle detector that looks like a process doing nothing, so it gets suspended, the gateway connection drops, and the bot shows offline until the next cold start wakes it. The result is a bot that responds whenever you test it and is offline by morning.
What a host has to give you instead is a supervised process rather than a terminal session. The container starts your process, watches it, and starts it again when it exits. That is what auto-restart on crash means on the plans here, and live console logs are what let you read the exit rather than guess at it.
Be clear about the limit of an automatic restart, though. It repairs a crash, not a bug. A bot that dies on a bad token or a missing import restarts into the same failure for as long as you let it, and a bot in a restart loop looks identical to a bot that is up unless you read the console.
- A restart also wipes memory. Anything held in a variable, a queue or a dictionary is gone; only what you wrote to persistent storage or a database survives, and the restart makes that loss invisible rather than obvious.
- An out-of-memory kill does not print a traceback. The log stops mid-line and the process exits 137, which is the container stopping you with SIGKILL rather than your code failing. If a log just ends, look at memory before you look at your handlers.
- Read the console, not only whether the bot appears online. A crash loop is the failure mode that most resembles uptime.
Sizing a bot: what 512 MB really holds
512 MB genuinely runs most bots, and the reason is that a Discord or Telegram bot spends almost all of its life idle on a socket. A discord.js or discord.py bot doing commands, roles, moderation logging and a few API calls is small: a runtime, a library, your handlers and a cache. What decides whether it fits is the cache, not the code you wrote.
That is the part people size wrong. discord.js keeps guilds, channels, roles and members in memory, so resident size tracks how many servers your bot is in and which gateway intents you enabled rather than how many lines you shipped. Turning on the members intent and fetching a full member list for one large guild is the single biggest jump most bots ever make. It is also why a bot that was comfortable at launch starts running out of memory later: the caches grew, your code did not.
Set an explicit ceiling while you are there. Node does not reliably size its heap from the container limit, so pass --max-old-space-size a little under your plan's RAM and let V8 raise a clean out-of-memory error instead of having the container kill the process without a stack trace. Python has no equivalent cap; it simply grows until the container stops it, which is why the log just ends.
Four things push you off 512 MB, and they are all the same thing underneath: something large that stays resident. Count every process you intend to run against the plan's RAM figure, not just the busiest one.
- A headless browser. Puppeteer or Playwright launching Chromium costs hundreds of megabytes for the browser plus each open page, and it spikes on page load rather than sitting flat. This is the most common reason a 512 MB bot dies.
- Image work. Decoding costs roughly four bytes per pixel whatever the file size, so one 6000 x 4000 photo is about 96 MB in memory per copy, and libraries make copies.
- An embedded model. Weights loaded locally stay resident for the life of the process, which is what the 4 GB plan is for. Calling a model API instead costs you almost no memory at all.
- A large in-memory cache. Anything you hold to avoid a database round trip is memory you pay for, and it has no upper bound until you give it one.
Secrets belong in the environment, not in your repository
Set tokens and API keys as container environment variables in the panel and read them with process.env or os.environ. That keeps them out of your code, out of your image and out of git. The reason to care is that a leaked bot token does more damage than the word token suggests.
A Discord bot token is not scoped the way an API key with per-endpoint permissions is. It is the bot. Whoever holds it can connect to the gateway and act as your bot in every server it has been added to, with whatever permissions those servers granted, and there is no confirmation step anywhere. If you were generous with the permission integer, that includes kicking, banning, deleting channels and mass-messaging members, all under your bot's name, in other people's communities. The cost of that is not downtime, it is your bot being removed and reported everywhere it ran.
Rotating the token fixes the future and nothing else. Whatever the holder read while they had it, member lists, or message content if you enabled that intent, is already copied. And deleting a token from your code in a later commit does not remove it: it stays in the git history, in every clone and in every fork. Rotate it in the developer portal rather than just editing the file.
- A crash reporter that dumps the environment. An error handler posting process.env, or a debug command printing configuration into a Discord embed, publishes the token to a channel. Log the key names, never the values.
- Secrets passed as command-line arguments. Arguments are visible in the process list. Environment variables are not printed by anything unless you print them.
- A token pushed to a public repository is often invalidated automatically, because the platforms scan for them. That is the lucky outcome. The same token in a private repo, a screenshot, a pastebin or a support message is scanned by nobody.
- Model provider keys leak quietly. A stolen bot token announces itself by misbehaving; a stolen model key just arrives as a bill. Set a spend limit at the provider.
Long-polling and webhooks ask for different things
Discord settles this for you: a Discord bot always holds an outbound WebSocket to the gateway. Nothing connects to it, so it needs no port, no domain and no certificate. Telegram lets you choose, and the choice changes what the host has to provide.
Long-polling is the getUpdates path. Your bot makes an outbound HTTPS request to Telegram with a long timeout, and Telegram holds it open until there is something to send. Everything is outbound, exactly like Discord, so it needs no inbound anything. It is also forgiving across restarts: Telegram keeps undelivered updates for up to 24 hours, so a bot that was down for a deploy collects what it missed when it reconnects. For most bots this is the sensible default, and it is the option that needs nothing special from us.
A webhook inverts the direction. Telegram makes an inbound HTTPS request to a URL you own, which means a public HTTPS endpoint on one of the four ports the Bot API accepts (443, 80, 88 or 8443) and a certificate it will trust. That is a setup step rather than a flag, so email hello@puranode.com before you build around it, rather than designing for an endpoint you have not confirmed. The full outbound network access on these plans covers the calls your bot makes; it does not by itself hand you an inbound HTTPS endpoint.
- The two are mutually exclusive. While a webhook is set, getUpdates returns 409 Conflict, and a forgotten webhook from an earlier experiment is the usual reason a long-polling bot receives absolutely nothing. Call deleteWebhook and try again.
- Only one poller per token. Running the same bot on your laptop and on the server at once produces the same 409, which is how most migrations go wrong on day one. Stop the local copy before you start the hosted one.
- Updates arriving while a webhook bot is restarting have nowhere to go. A long-polling bot picks them up afterwards. If you choose webhooks, that is the trade you are making.
What the six templates mean if you already have code
The templates are starting points, not frameworks you have to write against. Two are complete applications, three are runtimes with a sensible start command, and one is an escape hatch.
ClawdBot and Hermes are the finished ones. ClawdBot is a Claude-powered assistant for Discord and Slack with memory and tools, so the work is configuration rather than code, and its memory sits on persistent storage, which is what keeps it across restarts and deploys. Hermes covers the automation side: schedules, webhooks and workflows. A supervised long-running process is a far better home for scheduled work than a laptop's task scheduler, but write your schedules with an explicit timezone rather than assuming the container agrees with you about local time.
discord.js and discord.py are the bring-your-own-code paths. discord.js takes a Node project from git or a zip; install dependencies on the server rather than committing node_modules, and replace your local .env file with panel environment variables. discord.py installs from requirements.txt into a managed virtualenv, which makes pinning the thing that matters. An unpinned file resolves to today's versions on the server and to whatever you happened to install months ago on your machine, and that gap is most of what works on my machine actually means. Because the template gives you a Python runtime and a requirements.txt install, the library you import is your choice. Telegram bot is the long-polling or webhook path above.
Custom Docker is the one worth understanding, because it is the answer whenever a language template cannot express your runtime: a compiled Go or Rust binary, a bot that needs ffmpeg or Chromium's system libraries, or a dependency you would otherwise be asking a template to install for you.
- Build for linux/amd64. An image built on an Apple Silicon Mac defaults to arm64 and will not start on x86 hardware. Pass --platform linux/amd64, or build it with buildx.
- Run one foreground process as PID 1. An entrypoint that daemonises and returns looks like an instant clean exit, and the supervisor will restart it forever.
- Log to stdout and stderr. Anything written only to a file inside the container is invisible in the console you will be reading when something breaks.
- Keep state on persistent storage. The rest of the image filesystem is disposable by design, which is the point of an image.
The limits worth knowing before you pay
Everything runs in one region: EU-Central, Helsinki, Finland. For a game server a single region is a real constraint, because player ping is the product. For a bot it matters much less, because your users talk to Discord or Telegram and those platforms talk to us. Where it does show is the round trip to whichever model or API you call, which is a fixed cost from Finland on every plan, and in data residency, which for an EU business is the reason to be here rather than the compromise. The company behind it is MB VARIANTAI, registered in Lithuania, and prices include VAT.
The uptime figure on the plans is a target we run to, not an SLA with service credits. There is no compensation clause in our terms, and we would rather write that here than let a percentage imply a contract that does not exist.
Changing a plan's RAM is a manual resize we do the same day you ask, during business hours. It is not a slider you can pull at two in the morning, so if you already know a launch or a migration will need headroom, tell us beforehand rather than during. The Scale plan lists a dedicated IP; if your bot depends on an API that rate-limits or blocklists by IP, ask us what that means for your setup before you buy rather than assuming.
Keep your own copy of the code. Our terms say it plainly: we provide backup tools, and you are responsible for keeping your own copies of anything important. Treat the container as where your bot runs rather than the only place it exists, and git is the cheapest way to do that. There is no GPU on these plans, so if you need model inference on hardware we do not sell today, ask before you buy rather than after.