Back to Blog
Product Updates

Self-Hosting a Trading Platform: Docker, Backups, Security

QFQuantForge Team·April 3, 2026·8 min read

Running a trading platform on cloud infrastructure means trusting a third party with your exchange API keys, your trading logic, and your position data. For a platform managing real capital across 45 bots, that trust equation does not work. We self-host everything: the API server, the React dashboard, the bot engine, and all supporting services run on local hardware under our physical control.

This is not a philosophical preference. Exchange API keys with trade permissions are among the most sensitive credentials in crypto. A leaked key allows someone to place trades on your account, potentially draining capital through unfavorable trades. Self-hosting eliminates the attack surface of cloud providers, CI/CD pipelines, and third-party SaaS tools that would otherwise have access to these credentials.

API Key Security

Exchange API keys are encrypted at rest using Fernet symmetric encryption with PBKDF2 key derivation. The encryption key is derived from a passphrase stored in the environment file (.env), which is never committed to version control and exists only on the host machine.

When the server starts, it reads the passphrase from the environment, derives the encryption key, and decrypts the API keys into memory. The decrypted keys exist only in process memory and are never written to disk in plaintext. When the process stops, the keys are garbage collected.

The exchange API keys themselves are configured with trade-only permissions. No withdrawal permission. No transfer permission. Even if the keys were compromised, the attacker could place trades but could not withdraw funds from the exchange account. This is the single most important security measure: the API keys cannot move money off the exchange regardless of who holds them.

Network Security

The FastAPI server binds to localhost (127.0.0.1) by default. This means the API is only accessible from the local machine. No remote connections are accepted. The React dashboard connects to the API on localhost, and since both run on the same machine, this works without any network exposure.

For distributed backtest workers, we open the API to the local Tailscale network by setting API_HOST to 0.0.0.0. Tailscale provides an encrypted, authenticated WireGuard mesh network. Our two remote workers (cryptobotworker1 and mini-worker-1) connect via their Tailscale IP addresses. Traffic between machines is encrypted end-to-end. No ports are exposed to the public internet.

All API requests require a bearer token in the Authorization header. The token is a shared secret configured in the environment file on both the coordinator (Mac) and the workers. Requests without the token receive a 401 response. This is a simple authentication scheme, adequate for a single-operator system where the only clients are the dashboard and the workers.

Process Management

The API server is managed by launchd on macOS, the native process management system. A plist configuration file tells launchd to start the server at boot, restart it if it crashes, and keep it running indefinitely. The configuration specifies the working directory, the Python executable path, the command arguments, and the log file location.

Launchd provides several guarantees that matter for a trading platform. It starts the service before any user logs in, so the bots begin trading even if no one is physically at the machine. It restarts the service within seconds if the process exits for any reason (crash, out-of-memory kill, manual stop followed by start). It throttles restarts to prevent a crash loop from consuming system resources.

The React dashboard runs as a Vite dev server during development. For production, it would be built to static files and served by the FastAPI server itself or by a lightweight HTTP server. The dashboard is not mission-critical: bots trade regardless of whether the dashboard is accessible.

Database Backups

The SQLite database is the single most valuable file in the system. It contains all historical candle data (20.8 million rows), all trade records, all position history, all backtest results, and all risk events. Losing this file means losing the entire operational history of the platform.

SQLite's WAL mode complicates backups. You cannot simply copy the database file while the server is running because the WAL and SHM sidecar files may contain uncommitted changes. A direct file copy can produce a corrupt backup.

The correct backup procedure uses SQLite's built-in backup command, which creates an atomic snapshot of the database including any uncommitted WAL changes. The command reads the entire database while holding a shared lock, writes it to a new file, and releases the lock. The result is a self-contained, consistent database file that can be restored by simply copying it into place.

We run this backup daily via a cron job. The backup is stored on a separate volume from the primary database. We keep 7 daily backups, overwriting the oldest each day. For critical operations (like before running a large migration or importing new data), we take an additional manual backup.

Environment File Management

The .env file contains every secret the platform needs: exchange API keys, the Fernet passphrase, the API bearer token, the Telegram bot token, the Anthropic API key, and the Coinglass API key. This file is explicitly listed in .gitignore and has never been committed to version control.

The file is readable only by the user that runs the server process. File permissions are set to 600 (owner read/write only). No other user or process on the system can read the file. The environment variables are loaded at process startup and exist in memory for the lifetime of the process.

For the remote workers, each has its own .env file with only the variables it needs: the coordinator URL, the bearer token, and the worker ID. Workers do not have exchange API keys because they do not place trades. They only run backtest computations and report results to the coordinator.

Hardware Considerations

Our primary host is a Mac Mini with an M-series chip. The CPU handles the API server, bot engine, and dashboard simultaneously without significant load. The bots are I/O bound (waiting for exchange API responses), not CPU bound, so even during peak tick cycles with all 45 bots processing simultaneously, CPU utilization stays below 30 percent.

Memory consumption is modest. The Python process uses approximately 500MB including all loaded strategy instances, the SQLite connection pool, and the in-memory event bus. The React dev server uses another 200MB. Total system memory usage stays well under 8GB, leaving headroom for OS operations and occasional backtest jobs.

Storage is the primary resource concern. The SQLite database is approximately 5GB and growing. Candle data accounts for the majority. With 25 symbols across 11 timeframes and data going back to 2021, each day adds roughly 3,000 new candle rows. At the current growth rate, the database will reach 10GB within a year, still well within the capacity of the internal SSD.

What We Would Change for Live Trading

Paper trading with simulated fills is forgiving of many operational issues. Live trading is not. Before transitioning to live capital, we plan to add several hardening measures.

First, a hardware security module or encrypted USB key for the exchange API key passphrase. Currently, the passphrase is in the .env file on disk. A hardware module would require physical presence to unlock the keys on each server restart.

Second, a dedicated backup server with encrypted offsite replication. The current single-machine backup protects against database corruption but not against hardware failure or theft.

Third, network monitoring with alerts for unexpected outbound connections. A compromised process that attempts to exfiltrate API keys would produce unusual network traffic. Monitoring for this adds a detection layer beyond the prevention layers already in place.

Fourth, a read-only exchange API key for market data and a separate trade-only key for order execution. Currently, we use a single key for both. Separating them limits the blast radius if the market data key is compromised through a less-secured path (like a backtest worker that needs price data but should never place orders).

These measures are overkill for paper trading but essential for live capital. The self-hosting approach provides the foundation: physical control over the hardware, complete visibility into the software stack, and the ability to implement any security measure without depending on a cloud provider's feature set.