Skip to main content
uses a Self-hosted Daemon (also called the ModelDaemon) to execute build, run, and test commands on your own infrastructure. This is necessary when your project depends on private services - such as internal APIs, databases, or package registries - that are not accessible from the public internet.
The Self-hosted Daemon is for projects that need access to private infrastructure. If your project uses publicly accessible dependencies, use the cloud build environment instead - will auto-discover your lifecycle setup with no manual configuration needed.

ModelDaemon - Designed for Real Environments

The ModelDaemon runs directly inside your existing application environment - the same machine, the same runtime, the same network context your application already uses. There is no environment recreation, no containerization requirement, and no need to replicate your infrastructure elsewhere. This is intentional design. Enterprise applications depend on internal services, private registries, and runtime configurations that are difficult or impossible to faithfully reproduce on the public internet. ModelDaemon sidesteps that problem by sitting inside your network, where those services and credentials are already reachable — and it does so without polluting that environment with project-specific runtimes, because the agent and its dependencies live inside per-job isolated workspaces (see How It Works below). For teams with a stable internal network and a machine that can reach their private dependencies, ModelDaemon is the fastest and most reliable path to getting running.

When to Use a Self-hosted Daemon

  • Your app already builds and runs - no environment setup required
  • Dependencies are already in place - language runtimes, package managers, internal tools
  • You don’t need portability - the environment is stable and not going away
  • Speed matters - you want ModelCode operational with minimal infrastructure changes
V2 is a substantial rework of how the self-hosted daemon executes work. The biggest change is architectural — the AI morph-agent now runs on your daemon host instead of in our cloud — and that change is what makes the simpler onboarding possible:
AspectV1V2
Where the AI agent runsIn cloud, sending shell commands back to the daemonOn the daemon host, inside per-job isolated workspaces
Project dependenciesYou installed runtimes, package managers, and project tooling on the host yourselfThe agent discovers them automatically and the daemon materializes them as isolated pixi environments per project
Workspace isolationAll jobs ran directly in your host shellEach job runs in its own scratch directory under the daemon’s working tree, with its own pinned env versions
LLM provider keysn/a — agent ran in cloudThe daemon proxies LLM traffic back through , so no provider API keys live on your host
Encryption key pairYou generated and uploaded a public keyManaged automatically by the daemon — nothing to configure
Lifecycle commandsYou wrote install / build / run / health-check / test scripts manuallyAuto-discovered by the agent from your codebase, the same way the cloud environment does
Environment variablesYou added each variable in the UI before validationDiscovered alongside the lifecycle and editable later in the Lifecycle Setup page
End-to-end validation stepA separate “Validate Setup” wizardFolded into normal lifecycle discovery and milestone runs
The net result is that V2 onboarding is just three steps: name your daemon, run an install command, confirm it’s connected. Everything else is handled by the agent on first use.

How It Works

The ModelDaemon is a binary (mcode) that runs on a machine within your network. Once started, it:
  1. Registers with and begins sending periodic heartbeats
  2. Receives jobs from over an authenticated outbound connection — lifecycle discovery, spec extraction, milestone execution, code-review chat
  3. Spawns the morph-agent in an isolated workspace for each job — a fresh per-job scratch directory with its own pinned dependency environment
  4. Streams results, logs, and AI traffic back to through the same outbound connection
All communication is initiated by the daemon, so you do not need to open any inbound ports. The daemon executes each command in a new shell process on the host machine. There is no persistent shell session between commands - each step (install, build, run, test) starts a fresh shell -c "command" invocation. In V2, the AI morph-agent that drives migration work runs as a process on your daemon host. The daemon is responsible for setting up a clean, reproducible environment for it, executing it, and tearing everything down when the job finishes.

Per-job isolated workspaces

When dispatches a job, the daemon creates a fresh scratch directory under its working tree (<workdir>/jobs/<job-id>/) and starts a morph-agent HTTP server inside it. The agent operates on the repository clone in that directory, so jobs cannot see or interfere with each other’s files. When the job completes, the workspace and every process spawned inside it are cleaned up.
  • Environment variables - Configure environment variables in the Modelcode UI, not via lifecycle commands.
  • Working directory - The daemon manages the working directory and keeps it consistent across all commands within a single job. You don’t need to cd into the project directory unless a command should run in a nested directory.
  • Concatenated commands - When multiple commands must run in the same shell context, join them with &&.
  • Private repositories - The daemon executes lifecycle commands exactly as defined. If Composer requires authentication for private repositories, those credentials must be present on the daemon host or supplied via encrypted environment variables.
The daemon process itself runs commands with the same permissions as the user that started mcode. The agent runs inside its own pixi environment, so it does not inherit your host’s runtimes, but it does inherit network access, file-system permissions, and any credentials available to that user (SSH keys, kubeconfig, cloud-CLI tokens, etc.). Run the daemon as a user that has the network and credential access your project needs to reach private resources, and nothing more.

Setting Up the Daemon

Install the ModelDaemon on the same machine or environment where your application runs. This is what gives it access to your internal services, private registries, and runtime dependencies. Installing it elsewhere - even a similar machine - may result in build or test failures that are difficult to diagnose.The environment must remain stable for the duration of your project work.
One daemon per project. Each project supports a single active ModelDaemon. If your team has multiple seats, only one person should set up the daemon for a given project - typically whoever owns or has access to the target environment. Other team members connect to the same project and share that daemon automatically. Setting up multiple daemons for the same project is not supported and will cause conflicts.

Prerequisites

  • A machine on your network that can reach your private services
  • A account with an active project
  • Network connectivity from the machine to *.modelcode.ai over HTTPS (outbound only — no inbound ports required)
  • A POSIX-compliant operating system (Linux or macOS)
You do not need to preinstall language runtimes (Python, Node, Java, Ruby, Go, etc.) or package managers (pip, npm, maven, gem, etc.). The agent declares what each project needs and the daemon installs it inside an isolated pixi environment.

Supported Operating Systems

  • Linux (x86_64, ARM64) — Ubuntu, Debian, RHEL, Amazon Linux, SUSE, Alpine, and other POSIX-compatible distributions
  • macOS (Intel & Apple Silicon)
  • Windows — not currently supported

Onboarding Flow

The UI walks you through the full setup and generates the exact install command for your platform with your API key already filled in — follow the on-screen instructions. The flow boils down to three steps: name the daemon, run the generated install command on the daemon host, and click Verify & Continue so the platform confirms the daemon’s heartbeat.
  1. Generate an API key - Creates credentials for the daemon to authenticate with
  2. Install the daemon - Download and install the mcode binary on your target machine
  3. Set up encryption - Generate a key pair so the daemon can securely handle secrets
  4. Start the daemon - Run the daemon process and connect it to
  5. Verify the connection - Confirm the daemon is registered and online
  6. Configure lifecycle commands - Define the install, build, run, health check, and test commands for your project
  7. Configure environment variables - Add any variables your application needs at build or runtime, with optional secret encryption
  8. Validate the setup - Run an end-to-end validation that executes each lifecycle command in sequence
With a Self-hosted Daemon, lifecycle commands are configured at the project level during this onboarding flow. For the cloud environment, lifecycle configuration is also project-level but is auto-discovered - with separate origin and target entries, each containing scripts, variables, and a lifecycle document.
Once installed, you can sanity-check the daemon from the host: mcode version confirms the binary is present, and mcode logs tails the most recent log lines. Run mcode install-service followed by mcode start (the UI will remind you of this) so the daemon comes back up automatically after reboots.

After Onboarding

Once the daemon is connected, dispatches a first job that does the following on your daemon host:
  1. Materializes an isolated pixi environment based on the agent’s analysis of your codebase (manifests, lockfiles, framework conventions). On the very first project this also installs a shared pixi base layer; subsequent projects reuse it.
  2. Discovers the lifecycle configuration — install, build, run, health-check, and test scripts plus any environment variables — by reading the codebase and trying things in the isolated workspace. This is the same auto-discovery used for cloud projects.
  3. Surfaces the result in the UI. You can review and edit everything on the Lifecycle Setup page at any time.
Secret environment variables are encrypted in transit and at rest; the encryption keys are managed by the daemon itself, with no manual key exchange.

Migrating from V1

V1 daemons are deprecated, but they have not been switched off:
  • Existing V1 projects keep working. If a project is already connected to a V1 daemon, that pairing continues to run jobs as before. There is no forced migration and no deadline you need to meet.
  • New projects require V2. Any project created from now on can only connect to a V2 daemon. If you try to attach a new project to a V1 daemon, the connection will be rejected.
V1 and V2 cannot run side by side on the same machine. Both versions install the mcode binary and config to the same location (~/.local/share/modelcode/), so running the V2 installer on a host that already has V1 will replace the V1 daemon — your existing V1 projects will lose their daemon and stop running jobs.If you still have active V1 projects and want to onboard new V2 projects, install the V2 daemon on a different machine (any host with the same network and credential reach to your private services — see How It Works). Once your V1 projects are complete, you can replace V1 with V2 on the original host.
For reference on the V1 onboarding flow (encryption key generation, manual lifecycle commands, environment variables in the UI, validation wizard), see Self-hosted Daemon V1 (deprecated).

Why This Approach Works

Recreating an enterprise application environment from scratch is harder than it looks. Internal services have specific versions, configurations, and network paths that are rarely fully documented. Private registries require credentials that may be tied to specific machines or users. Runtime behavior often depends on system-level configuration that isn’t captured in source control. ModelDaemon avoids this entirely. It runs in your real environment - not a simulated one. The commands it executes are the same commands your developers run. The network paths it uses are the same ones your application uses in production. The credentials and config files are already in place. You get the correctness of an in-environment setup (real network, real credentials, real registries) without the operational cost of treating the host as part of the project’s runtime stack.

Environment Stability

ModelDaemon is designed to run on a stable host. The environment where the daemon is installed should:
  • Remain accessible for the duration of the project
  • Maintain consistent build and runtime behavior across sessions
  • Not be decommissioned, re-imaged, or significantly reconfigured mid-project
This is a strength, not a constraint. The daemon’s reliability comes directly from the stability of the environment it runs in. Teams that treat their build environment as a managed, long-lived resource get the most consistent results.

Example: Running ModelDaemon in an Existing Application

A typical self-hosted daemon deployment looks like this:
  1. The daemon binary is installed on the same server or VM where the application is already running
  2. The daemon is configured with an API key and pointed at the Modelcode platform
  3. Lifecycle commands - install, build, run, test - are defined in the Modelcode UI using the same commands the team already uses
  4. No infrastructure changes are made: no new VMs, no containers, no network modifications
  5. ModelCode begins executing build and test workflows immediately, using the existing environment
This setup is in production use across applications with complex dependency chains, private Artifactory registries, and internal service dependencies. The common thread: the environment was already working, and ModelDaemon leveraged that directly.

Troubleshooting

Daemon Shows as Offline

The daemon appears offline when has not received a heartbeat within the last few minutes. Check if the daemon process is running:
ps aux | grep mcode
If the process is not running, start it again with mcode start. Check network connectivity: The daemon must be able to reach the server over HTTPS. Verify with:
curl -I https://<YOUR_MORPH_HOST>
If this fails, check your firewall rules, proxy settings, or VPN configuration. Check the daemon logs: Run mcode logs to show the most recent log lines. Logs are also written to the logs/ directory next to the mcode binary. Look for connection errors or authentication failures. Common causes:
  • The host rebooted and mcode install-service was never run, so there is no service manager keeping the daemon up
  • A network change (VPN disconnect, firewall rule update) is blocking outbound HTTPS traffic
  • The API key was rotated in the UI but not updated in the daemon’s config.toml

Command Not Found

The daemon inherits the PATH from the shell that started it. When installed as a system service, it takes a snapshot of the current PATH and stores it in its config file. If you install new tools after the daemon is already running as a service, update the stored path with mcode refresh-path and restart the daemon.

API Key Issues

Test Generation Failed

Test generation failures occur when cannot generate tests for your project. This typically relates to the build or run steps. Application failed to build: Check the install and build command output in the validation logs. Common causes:
  • Missing dependencies that are only available on your private network
  • Incorrect build command - verify it works when run manually on the same machine
  • Environment variables missing - add them in the Modelcode UI
Application failed to start: If the build succeeds but the run command fails:
  • Check that the port your application listens on is not already in use
  • Verify that required services (databases, APIs) are reachable from the host machine
  • Review the run command output for startup errors
Health check timed out:
  • Verify the API key in the daemon’s config.toml matches the one shown in the UI
  • If the key was rotated, update config.toml and run mcode start again

FAQ

Does the ModelDaemon Support Windows?

No. Windows is not currently supported.

Do I need to install Python, Node, Java, or other runtimes on the host?

No. The agent declares each project’s dependencies in a manifest and the daemon installs them inside an isolated pixi environment under ~/.local/share/modelcode/. Your host only needs to be able to reach the network locations your application reaches.

Where does the AI agent actually run?

On your daemon host, inside a per-job pixi-managed workspace. The daemon spawns one morph-agent process per job, gives it an isolated working directory and pinned dependencies, and tears everything down when the job finishes. cloud orchestrates the work but does not execute it.

Do you store my code or my LLM provider keys in the cloud?

Source code is read by the agent on your daemon host and never copied off it as part of normal job execution. The daemon proxies the agent’s LLM calls through using your daemon’s API key, so there are no provider API keys (Anthropic, OpenAI, etc.) on the host.

Can I run multiple daemons on the same machine?

No. The installer always writes to ~/.local/share/modelcode/, so running it a second time on the same host (under the same user) replaces whatever daemon is already installed there — whether it’s a V1, an older V2, or a V2 registered under a different name. To run more than one daemon at the same time, install each on a separate machine.

Do I still need to configure lifecycle commands manually?

No. With a V2 daemon, the agent auto-discovers your lifecycle the same way it does for cloud projects. You can review and edit the discovered scripts and environment variables on the Lifecycle Setup page at any time.

Do I still need to upload an encryption public key?

No. V2 daemons manage their encryption keys internally. There is no mcode encryption generate step and no public key field in the UI.

Does the ModelDaemon Support Windows?

Plan for ~5 GB under ~/.local/share/modelcode/ for the daemon binary, pixi, and a small project’s environments. Larger monorepos or projects with many declared environments (e.g. one per service) will use more — pixi caches conda packages on disk, and the daemon keeps prior environment versions around as long as some workspace still pins them.

Next Steps