Setting up a VPS for a bot: where the environment breaks
Setting up a VPS for a bot usually starts the same way: code is uploaded to the server and run manually from the terminal.
While the SSH session is open, everything works — but as soon as you close the window or the connection drops, the broken session sends the process a termination signal. The bot crashes without an error in the logs: it simply stops responding to exchange events.
A separate class of failures is server reboot. Provider maintenance, kernel update, host restart: the bot does not come up on its own because it is neither in autostart nor under a supervisor.
Typical failures: session drop, reboot, hung processes
Along with the process, state that lived only in memory is lost — timers, open positions, order cache — and recovery turns into manual log analysis.
Hung instances accumulate even more unnoticed: after another crash the bot is started again manually, the old process remains hanging, holds the connection and port, and the new one cannot take the same resource.
The symptom is always the same — "the bot is not responding" — but by that point there are already three or four causes.
This is resolved by moving startup into a systemd unit: autostart on machine boot, restart policy on failure, a separate log per service. State is moved outside the process memory, and orphaned instances are killed by service identifier rather than by searching the task list. Next we break down how this is configured step by step.
What a managed environment gives: measurable effect
A managed environment differs from running a bot "in the terminal" not by convenience, but by predictable behavior during a failure. Below is what is measured before and after, without wording like "it became more reliable."
Autostart and self-recovery
A systemd unit with Restart=always policy brings the process up after a crash, VPS reboot, and exchange connection drop; restart rate limiting prevents the bot from entering an infinite loop.
Recovery time after a process failure is reduced from minutes of manual work to seconds — the operator learns about the fact, not the consequences.
Practical effect: the share of failures covered by autostart reaches 80–90% — what remains is configuration errors and exhausted API limits, which are handled manually.
A watchdog timer checks not "whether the process is alive" but whether the bot responds to requests, and restarts a hung instance.
Observability: logs and alerts
Bot monitoring is built on structured logs with rotation: the history of trades and errors is not lost on restart and does not fill the disk entirely.
Metrics — API response latency, share of rejected orders, queue size — feed alerts with thresholds, so degradation is known before it turns into losses.
Bottom line: manual interventions move from "several times a week" to "once a month or less," and the path from failure to a working service takes seconds instead of tens of minutes.
Bot deployment options: comparison of modes
The deployment scheme is chosen not by machine power, but by the number of roles the bot has: the trading core, market data ingestion, state storage, and notifications produce different load profiles. Below are four real schemes and their applicability boundaries.
| Option | When it fits | Limitations |
|---|---|---|
| Shared hosting | Trial run, one thread without state storage | No persistent process, restart on timeout |
| One process on a dedicated server | One trading loop, state local | A crash stops everything, updates require downtime |
| Separate services | Core and data collection with different loads | More connections and points of failure |
| Isolated containers | Multiple bots, different environments | Need volumes for state and memory limits |
When one process is enough
One process with a local database is justified as long as the bot serves a single trading loop and restart takes seconds. What decides is not server power, but predictability: short startup, state recovery from a file, monitoring reduced to liveness checks and log analysis. As long as these conditions hold, splitting will only add connections.
When roles and data need to be separated
With a second loop, heavy market data collection, or notification broadcasting, a single process starts getting in its own way: collection consumes resources, and restarting the core breaks subscriptions.
Then roles are moved into separate services, state into external storage, and containers pin the environment so that updating one part does not affect the others.
Setting up a VPS for a bot: stages of engineering work
The order of work is structured so that after handover the bot runs without manual intervention: it survives a server reboot, an exchange connection drop, and a single process crash.
We start with an audit of the current startup — otherwise it is easy to lose position state on the very first restart.
Audit and deployment scheme
- Startup audit: how the bot starts now, where access keys are stored, where logs are written, what happens on reboot. We record risks — manual startup, secrets in plain text, no limits.
- Deployment scheme: we lay out roles — trading logic, queue handlers, state database. Separately we decide what is stored on disk and what in the database, so that a restart does not lead to duplicate orders.
- Baseline security: a separate user without admin rights, key-based login, closed inbound ports except the service one, automatic updates, and fail2ban against brute force.
Fault tolerance testing
- Autostart: a systemd unit with a restart policy, memory limits, and a separate environment file accessible only to the owner. Logs go to journald with rotation — the disk does not fill up.
- Monitoring: process metrics, exchange response latency, scheduled liveness checks, messenger alerts by thresholds — crash, error growth, downtime longer than specified.
- Failure scenarios: we kill the process, reboot the server, break the network, roll back a release — we check whether the bot restores the connection and does not duplicate orders. We run them again after every change.
- Handover to operation: instructions for updates, rollback, and typical failures, access for the customer's team, walkthrough on a live stand.
Configurations, units, and regulations: handover content
Handover is not an archive, but the working state of the project: after acceptance the bot is operated without us, and everything needed for that lies in the customer's repository, not in chat.
We record the content in the acceptance act — using it we also check that the build comes up on a clean machine in one pass.
What is in the repository
- Bot configuration files: startup parameters, connection addresses, market identifiers — configuration values are separated from code and supplied from the environment; there are no secrets in the repository.
- Service manager autostart unit: restart strategy on crash, memory and CPU limits, dependency order, logging to the system journal.
- Access policies: network filter with closed inbound ports, a separate unprivileged user for the service, trading key restrictions — only for required operations, without withdrawal rights.
- Keys and tokens — in secure storage, not in text files and not on the command line: this way the secret does not remain in process history and logs.
- Backup scheme: what is copied, how often, where copies are stored, how a copy is deployed on a backup server.
Regulations: updates, key rotation, backups
- Updates — through a test loop warmed up on historical data, a maintenance window, and a fixed rollback order to the previous build.
- Key rotation — on schedule and off-schedule when team composition changes; replacement happens without stopping the bot, the old key is revoked after confirming the new one is in use.
- Backups — with recovery verification, not just with the fact of export: once a quarter we bring up a copy and reconcile position and trade state.
- Incident analysis: where to look at logs, which metrics count as emergency, whom to escalate to and within what timeframe.
Describe the task — we will come back with an estimate and a plan.
Case study: how the bot stopped crashing and losing state
The client came with a bot that we took over for support under the working name Arbitr: a trading algorithm on a virtual server, launched manually from the terminal. First we analyzed the history of stoppages — it explained almost all complaints about trading results.
What it was before
The process had no watchdog service: when the exchange connection dropped or an unhandled exception occurred, the bot terminated, and no one knew about it until morning.
In a month there were up to seven nightly stoppages of 40 minutes or more, and after a manual restart positions had to be reconciled with the exchange by eye. State was held only in memory, so every crash meant losing open trades and a accounting discrepancy.
What changed after
The setup case boiled down to three things: the bot was made into a systemd service with automatic restart and attempt rate limiting, state was moved to a local database with disk writes after every trade, and event logs and failure alerts were sent to a messenger — the signal arrives at the moment of stoppage, not in the morning.
Stable operation stopped being a manual operation: uptime share grew from 92% to 99.9%, recovery after a failure takes seconds, manual restarts per quarter are zero, and discrepancies with the exchange on open positions are zero as well.
What to do if the bot is already deployed on shared hosting?
Shared hosting keeps the bot only until the first failure: there are no rights to manage services, background processes and timers are cut down, and when someone else's application restarts, the bot simply does not come up.
State lives in a single file with no direct network access — there is nothing to restore it with after an accident.
First the state is transferred, not the code: the database with trade history and balances, the file with keys, connection settings, and working logs.
The code is deployed as is and made into a service with autostart and restart on crash — there is no need to rewrite the logic for the new server if it is not tied to hosting limitations.
To avoid downtime, the new copy is brought up alongside: it runs on a database snapshot and does not place orders. Then the streams are switched — first market data and signal ingestion, then execution.
During this time the old node closes out already open positions, so the gap in operation is unnoticeable.
Before switching, what accumulates in the background is transferred: open orders, venue limit counters, active sessions, unfinished transactions. Each record is reconciled with the position log — a discrepancy in even one line will lead to an incorrect balance calculation.
Static assets, the web panel, and log archives can be left as is — they do not affect trading. The transition order is simple: prepare the environment, transfer data, run the bot idle, switch data ingestion, and only then execution.
For a couple of days the old node is kept as a fallback with orders disabled, and only after that are its accesses closed.
Technical doubts: questions about bot operation
Operational questions appear not at the start, but after a few weeks of operation: what will happen on reboot, how to learn about a crash, how to update logic without losing state.
Each of them is addressed by configuration, not promises, so we answer concretely and with applicability boundaries.
What happens when the server reboots?
The process is described by a unit file with autostart and a restart-on-failure policy. We keep state in external storage: after startup the bot restores positions and open orders from the log, no manual assembly is needed.
How will I know the bot crashed?
We collect signs of operation, not just process liveness: freshness of the last trade, lag behind market data, connection errors. On deviation, an alert goes to messenger and email — we learn earlier than the eye would notice.
Can the bot be migrated without downtime?
We bring up a second loop alongside, transfer data, switch the load, and only then shut down the old one. Downtime fits within minutes, and trade history and settings are fully preserved.
Why move data to a separate server?
Then exports and backups do not compete with trading for resources, and access to storage is restricted by separate credentials. Compromise of the bot does not open access to the database.
How to update logic without risk to the production version?
The build goes through a test loop with historical data, then release to production, and the previous version remains ready for rollback. We make data schema changes reversible.
Let's discuss the task: what to send for an estimate and plan
Estimating work to set up a server for a trading bot is never templated: the scope depends on the current state of the site and how strict the continuity requirements are.
To discuss the task concretely rather than in general terms, a few project inputs are enough — from them we assemble an implementation plan without assumptions and unnecessary questions along the way.
- Where the bot currently runs: your own machine, a cloud server, or a local workstation, what operating system, and how the process is started — manually, via a supervisor, or as a service.
- What failures have already occurred: process crashes, reboots, exchange connection drops, missed signals — and how you learned about them.
- How data is stored: access keys, position state, trade history, backups, and where they are stored.
- Who has access to the server: how many people, with which keys, whether there is a shared password or login via control panel.
- Downtime requirements: acceptable unavailability time, whether automatic restart is needed, and how to handle desynchronization with the exchange.
- What should go into monitoring: error notifications, key metrics, and which on-call person responds.
Send these inputs — we will come back with a work estimate and implementation plan: stages, timelines, checkpoints. We will also say what from the current configuration can be left unchanged, so as not to redo what already works reliably.







