Hosting, backups & troubleshooting
Key takeaway: Heygents is a single Node server plus a browser bundle. You clone it, run npm install, write a .env file, and run it under a process manager behind a reverse proxy that terminates HTTPS. All mutable state lives in an untracked data/ directory backed by plain JSON files or an opt-in SQLite backend, and a nightly script backs it up.
Heygents is a single Node server plus a browser bundle. This page is for whoever operates the install: how to set it up, keep it running, back it up, connect projects that live on another machine, deploy new code, and diagnose the handful of things that actually go wrong.
Heygents is fully hosted. At heygents.dev the hosting, upgrades and backups on this page are handled for you and there is no server for you to operate. The one piece you do install is the companion agent on your own machine - see Installing the companion agent. The rest of this page is an operations reference for how the service runs.
Setup
Installing is deliberately unremarkable. Clone the repository, install dependencies, and create a configuration file:
cd /opt/hub
npm install
# create .env - see Environment variables below
There is nothing to seed. Every reader of the runtime state defaults gracefully to an empty list or object when a file is missing, so a fresh checkout boots into a working, empty Heygents. If you want starter rows, copy the committed example project file into place; otherwise just add your first project from the interface.
All mutable runtime state - the project list, tasks, chat history, uploads, automations, the health snapshot - lives under a data/ directory that is intentionally not tracked in source control. That is what lets a deploy pull new code without ever conflicting with data the live server is writing at the same moment.
Running the server
The server binds to loopback on port 9100 and is meant to sit behind a reverse proxy that terminates HTTPS. HTTPS is not optional if you want phone push, which the browser only permits on a secure origin. It runs under a process manager:
pm2 start cloud/server.js --name hub-cloud # first time
pm2 restart hub-cloud # after changes
pm2 logs hub-cloud # tail the log
A restart is safe to run at any time. Heygents is the parent of every AI run it launches and holds the live output stream to your browser, so it drains gracefully on a restart signal: endpoints that would start a new run answer that the server is draining, live runs and their attached streams keep going, and the process exits only once the last in-flight run has finished. Clients reconnect automatically once the server is back.
Project health and host metrics are collected by the server itself rather than by a cron job, so there is exactly one writer to the status file. The collection intervals are editable from the interface under the automation frequency settings, and a change takes effect within a minute with no restart.
Environment variables
Configuration is a single .env file. It holds live secrets and is never committed; if one leaks, rotate it. The server itself needs no application variables to boot - the AI work shells out to the provider command-line tools installed on the machine, using their own credentials. The ones that matter in practice:
- Auth service configuration - the URL and public key of the Supabase auth service that owns the accounts, plus the secret used to verify the access tokens the browser presents. There is no login passphrase and no shared sign-in secret: people sign in with an email and password or with Google or GitHub, and every request is verified against their own account. Sign-in and password-reset attempts are rate limited by the auth service itself, so there is no lockout window to tune here.
- Google and GitHub OAuth credentials - the client id and secret for each provider you want to offer on the sign-in card. Leave a pair out and that button simply does not appear.
- Agent pairing - nothing to configure. Pairing tokens are minted per machine from Install agent inside the app and travel in the one-line install command, so there is no shared agent secret in the configuration file.
- Push keys - a generated keypair for web push. Generate it once with the bundled script. Without it the notifications panel still renders but nothing sends.
- Provider binaries - explicit paths for the Codex and Gemini command-line tools when they are not on the service's PATH. The Gemini tool is a separate install; without it, chat turns sent to Gemini simply fail to start.
- GitHub credentials - the OAuth app or GitHub App identifiers that light up the GitHub panel. Absent, the panel reports that the integration is not set up.
- Storage selection - which storage backend to use and where the database file lives.
One deliberate non-variable: provider API keys. Anthropic and Google keys are stripped from every AI process Heygents launches, unconditionally in Gemini's case, so Heygents can only ever spend a subscription or OAuth session and can never silently start billing a metered API.
Storage backend
By default all data lives in plain JSON document files under data/. Each write is a read-modify-write of a whole file made atomic by writing a temporary file and renaming it. This needs no configuration and is easy to inspect, copy and diff.
There is an opt-in SQLite backend for installs that have grown. It stores exactly the same document shapes, so the API responses and the browser bundle are identical either way - only where the bytes live changes. The win is transactional single-writer writes with a write-ahead log, meaning no torn files if the machine dies mid-write, plus a real query surface. Cutting over is a one-time migration followed by setting the storage variable and restarting; the migration is idempotent, never deletes the JSON files, and rolling back is just removing the variable and restarting.
Two details are worth knowing regardless of which mode you pick. Task-run transcripts always use their own SQLite table, because that file had grown past a megabyte and was being rewritten in full on every completed task. And the health snapshot file is always plain JSON in both modes, because it is written by the separate heartbeat collector.
Backups and restore
Everything you would miss lives under data/, and it is written by the live server rather than by any editor, so a backup has to be a real scheduled job. The bundled backup script runs nightly from cron and:
- Snapshots the entire
data/tree - document stores, chat history and uploads - into a timestamped compressed archive. - Verifies the archive's integrity immediately, so a corrupt backup is caught the night it is made rather than the day you need it.
- Keeps two weeks of archives locally and mirrors them offsite to cloud storage with a 90-day retention.
- Logs every run and raises an alert on failure.
You can run the same script on demand before any risky change. To restore, pick an archive from the local directory or pull one back from the offsite copy, extract it somewhere scratch, and copy the specific files you need back into data/. Restoring selectively is usually right - you rarely want to roll all of Heygents back just to recover one project's chat history.
Monitoring
Heygents exposes a health endpoint and a metrics endpoint, the latter optionally protected by a bearer token and in any case only reachable on loopback. Those are the two hooks to point your own uptime checker at.
The dashboard's own numbers - CPU, memory, disk, uptime, and per-project reachability - come from a heartbeat collector that the server spawns on a configurable interval. That is what fills the stats header and the health dots next to each project. Separately, a host guardian watchdog runs on a short timer and automatically relieves resource pressure: truncating a runaway container log, pruning build caches, vacuuming journals, or stopping a container that is crash-looping. When it acts, the dashboard shows a dismissible amber notice and plays its own notification chime, and a summary is emailed to the configured recipient. If no mailer is installed the email step is skipped and logged, and everything else still works.
Historically there were also three watcher daemons alerting to a chat service for crash loops, application health and host metrics. These were switched off at the operator's request and there is no replacement alerting in place. Restoring them is a matter of starting the three scripts again under the process manager.
Local, off-server projects and the companion agent
Not every project lives on the server - and on Heygents none of them do. A project marked Local machine lives on another computer, a laptop or your own server, whose filesystem this server cannot reach, so no AI process can be launched here for it. The answer is a small companion agent that runs on that machine, connects outward to Heygents, and runs the AI tool locally in the project's real folder. Its output streams back through the normal run pipeline, so the chat behaves exactly like an on-server project.
Installing it is one command on the other machine, carrying a pairing token generated from Install agent in the app. The installer detects the operating system and writes the appropriate background service - launchd on macOS, systemd on Linux, and the equivalent on Windows - so the agent survives reboots, and re-running the same command upgrades it. The machine needs Node 18 or newer and a logged-in AI command-line tool; the agent is OAuth-only and never receives a provider API key. The full walkthrough is in Installing the companion agent.
For a Local project the interface adapts: the database section is hidden, an optional agent host field appears, and the working-directory browser asks the agent to list the other machine's filesystem rather than the server's. The chat header shows a green or red chip telling you whether that project's agent is currently connected, which is the first thing to check when a local project stops responding.
One server, every project
Heygents runs as a single Node process behind your own proxy, keeps its state in files you can copy, and reaches projects on other machines through a small companion agent.
Open Heygents →Build & deploy
The browser bundle is the only build step in the repository; the server runs directly with no compilation. Production deploys should go through the checked-in deploy script so that code updates, dependencies, bundles, tests and the restart always happen in the same order:
cd /opt/hub
npm run deploy:prod
It refuses to run if there are uncommitted code or configuration changes, while tolerating the constant churn in data/. Then it fetches and fast-forwards the current branch, installs dependencies exactly as locked, rebuilds the browser bundles, runs the test suite, and finally requests the guarded delayed restart rather than killing a running task. Overrides exist for deploying a specific branch, deploying the current checkout without pulling, adjusting the restart delay, and - for emergencies only - skipping tests.
The test suite also runs on every push and pull request in continuous integration, and a local pre-push hook can be enabled per clone so a failing suite blocks the push before it ever reaches the remote.
Common problems
- The app drops back to the sign-in card mid-session. The access token expired and could not be refreshed - usually because the refresh token was revoked or the auth service was unreachable. Sign in again; if it repeats for everyone at once, check that the auth service is up and that its verification secret matches the one the server is configured with.
- Phone push went silent everywhere at once. The push keypair was regenerated. Subscriptions are bound to the key they were created with, so all of them are now invalid. Check the log for repeated send failures, then press Enable again on each device. The panel detects a stale subscription and heals itself.
- Gemini chat turns fail immediately. Either the Gemini command-line tool is not installed on the server - it is a separate install from the others - or its login session has expired. It is intentionally never allowed to fall back to a paid API key, so a missing session is a hard failure rather than a silent charge.
- A project shows as "not monitored from this server". Its health row was dropped from the status snapshot. Rows for projects the collector does not itself manage are written by Heygents, so a scan of that project restores it.
- A restart killed a running task. Something restarted the process directly instead of using the guarded restart. Always use the guarded path from inside a Heygents task.
- A local project stopped responding. Check the connection chip in the chat header. A red chip means that machine's companion agent is not currently connected; restart the agent service on that machine.
- Sign-in refused with "invite-only". The beta only admits invited addresses, so signing in with an uninvited email reports that rather than creating an account. Invite the address, and the invite email lands the person on a set-a-password panel.
- A chat page feels slow to load. A busy project's full chat history is large. Make sure your reverse proxy compresses responses - the application itself does not.
Frequently asked questions
Does Heygents need a database?
No. By default all data lives in plain JSON document files under data/, with every write made atomic by writing a temporary file and renaming it, which needs no configuration and is easy to inspect and copy. An opt-in SQLite backend stores exactly the same document shapes, so the API responses and the browser bundle are identical either way.
Can I run it without HTTPS?
You can, but phone push will not work, because browsers only permit web push on a secure origin. The server binds to loopback on port 9100 and is meant to sit behind a reverse proxy that terminates TLS, so adding HTTPS is a proxy configuration step rather than an application change. Let that proxy compress responses too, since the application does not.
What do I actually need to back up?
The data/ directory and your configuration file - everything else is in source control. That directory holds the project list, tasks, chat history, uploads, automations and the health snapshot, and it is written by the live server, so a backup has to be a real scheduled job. The bundled script archives it nightly, verifies the archive, and mirrors it offsite.