Deployment
This page deploys a self-hosted DAIV with Docker Swarm or Docker Compose, puts it behind an Nginx reverse proxy, and creates the first admin. Afterwards you have a DAIV instance ready to connect to your codebase.
To try DAIV on one machine first, follow the Quickstart.
Requirements
| Requirement | Version |
|---|---|
| Docker Engine | The samples pin no minimum. Docker Swarm needs Swarm mode enabled on the host. |
| Docker Compose | 2.17.0 or later, which provides the docker compose command and depends_on.restart. Only the Compose deployment needs it. |
| PostgreSQL | 15 or later, the minimum of the Django release DAIV pins; the samples run postgres:17.6. |
| Redis | 8 or later. The samples run redis:8.6-alpine. The agent's checkpoints use Redis search commands that Redis 7 lacks, and they must stay on database 0; see Redis. |
| daiv-sandbox | The samples pull ghcr.io/srtab/daiv-sandbox:latest. Update the sandbox together with DAIV: multi-repository sessions need a release with repos_archive support and path-scoped egress rules, and DAIV reports an error when the sandbox lacks it. |
You also need a domain name that points to your server, with an SSL certificate for it, and a way for your Git platform to reach DAIV_EXTERNAL_URL. Your Git platform sends webhooks to that URL.
Services DAIV runs
DAIV needs these services. The samples below run all of them:
- PostgreSQL stores application data.
- Redis holds the cache, sessions and the agent's checkpoints.
- DAIV app (
app) serves the web dashboard and the API. - DAIV worker (
workerandworker-interactive) runs background tasks. - DAIV scheduler (
scheduler) runs periodic tasks, among them adding webhooks to your GitLab projects and syncing repository access. - DAIV sandbox (
sandbox) runs the agent's commands and git in isolated containers, through an egress proxy that holds the git credentials. See Sandbox.
Task queues
Background work is split in two: default carries agent runs, which hold a worker for as long as the run lasts, while interactive carries short user-visible work: session titles, run classification and notification delivery. The worker and worker-interactive services each serve one of them, so a title never queues behind a run. A worker started with no queue argument serves every queue, which suits a small deployment that accepts that wait.
Deploy with Docker Swarm
Docker Swarm gives you service discovery, load balancing and rolling updates, so it suits production. These steps deploy to a single server; to extend the stack to more servers, see the Docker Swarm documentation.
Before you start, install Docker and enable Swarm, and check the requirements.
-
Create the first seven Docker secrets listed in Swarm secrets, with this command (see Docker secret create), replacing
<name>and<value>:Bash -
Create the egress CA. The sandbox reaches your Git platform only through a per-session proxy that adds the credentials, so the token never enters the sandbox, and the proxy signs with this CA:
-
Store the CA as the two secrets the stack reads:
-
On each node, add this to
/etc/docker/daemon.json. Each agent run holds many concurrent sockets (sandbox, LLM API, tracing, database, Redis, git over HTTPS), and Docker's default soft limit of 1024 open files fails under load with[Errno 24] Too many open files. Swarm ignores service-levelulimits, so the daemon must raisenofile: -
On each node, restart Docker:
Bash -
Save the Swarm stack file as
stack.yml, and replace each annotated value with your own. Environment variables lists the options. -
From the directory that holds
stack.yml, deploy the stack:Bash -
Wait until every service is healthy, which takes several minutes while Docker pulls the images and DAIV migrates the database:
Bash -
Put DAIV behind Nginx, so you can reach it over HTTPS.
-
Create the first admin by running
bootstrap_adminin the app's container, replacingadmin@example.comwith the admin's email address:Bash -
Open
/accounts/login/and sign in with the one-time code DAIV emails to that address.
If you turn on OAuth login before anyone signs in, the first Git platform sign-in becomes the admin instead. The first admin explains both paths, and Accounts and roles covers adding users.
Swarm secrets
The stack reads these Docker secrets. Create the first seven rows in step 1, and the next two in steps 2 and 3. The last two are optional.
| Secret | Holds |
|---|---|
django_secret_key |
A random secret key for Django, such as the output of openssl rand -hex 32. Keep it backed up: see Django. |
db_password |
A random password for the PostgreSQL database. |
codebase_gitlab_auth_token |
A GitLab personal access token with the api scope. See Platform setup. |
codebase_gitlab_webhook_secret |
A random secret that GitLab sends with each webhook, for DAIV to validate. |
daiv_sandbox_api_key |
A random API key for the sandbox service. |
email_host_password |
The SMTP password. DAIV authenticates only when EMAIL_HOST_USER is also set. The stack requires the secret to exist, so create it with any value if your relay needs no authentication. |
openrouter_api_key |
An OpenRouter API key. |
daiv_sandbox_egress_ca_cert |
The egress CA certificate. |
daiv_sandbox_egress_ca_key |
The egress CA private key. |
allauth_client_id |
Optional. The OAuth client ID of your Git platform. See Authentication. |
allauth_client_secret |
Optional. The OAuth client secret of your Git platform. |
A secret locks the matching field in the dashboard: DAIV reads the value from the secret and you cannot edit it under Configuration → Authentication. When you will turn on OAuth login in the dashboard instead, leave allauth_client_id and allauth_client_secret out of the app service's secrets: list and out of the top-level secrets:, as the stack file below does. To use them, create both secrets, then uncomment those lines.
For another provider's key, create a secret named after its variable in lowercase (openai_api_key, anthropic_api_key or google_api_key) add it to the secrets: list of the app service, which runs the migration that reads it, and declare it under the top-level secrets: with external: true. DAIV reads each provider key once, at its first start; see Provider API keys. For further variables you want as secrets, see Environment variables.
Swarm stack file
| YAML | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
- Replace
daiv.example.comwith your domain name, without the scheme (daiv.example.com, nothttps://daiv.example.com). Keepappand127.0.0.1for communication between services. - Replace with your full URL, including the scheme (for example
https://daiv.example.com). Your GitLab sends webhooks to this URL, so it must be able to reach it. - Set to your GitLab instance URL (for example
https://gitlab.comfor GitLab.com). - Points to the sandbox service. Use
http://sandbox:8000when the sandbox runs in the same stack. - Continuous deploys track the
maintag. Keep the${DAIV_IMAGE_TAG:-main}form on every service, so the Deploy workflow can roll back to a pinned per-commit tag (for exampleDAIV_IMAGE_TAG=sha-XXXXXXX). - Required: the sandbox needs Docker socket access to create isolated containers.
- Optional: remove this volume if you don't need private registry access.
- Scaling: increase
replicasto handle more concurrent agent runs (for examplereplicas: 3). A worker runs one task to completion before it claims the next, so concurrency is the number of worker containers. Each claims independently from the queues named in its command. - Optional: uncomment all three lines to mount custom global skills that are available across all repositories.
appreceives uploads from the dashboard, so it mounts the directory read-write;workerandworker-interactiverun the agent, so they can mount it read-only. - Required: the web and worker containers must share this directory. It holds the artifacts (reports and other files) the agent publishes:
appwrites them for chat turns and serves them, andworkerwrites them for jobs and webhook runs. Any shared filesystem works; on a multi-node swarm, use a network-backed volume driver. - Redis 8 or later; see Requirements.
- Required: the egress CA you stored as secrets in step 3 of Deploy with Docker Swarm. The sandbox's egress proxy signs with it; without it, every agent run fails when the sandbox rejects the session.
- The SMTP user. DAIV sends the
email_host_passwordsecret only when this is set; delete the line if your relay needs no authentication. Without an SMTP server, replace theEMAIL_*lines withEMAIL_BACKEND: django.core.mail.backends.console.EmailBackendand read the sign-in code fromdocker service logs daiv_app. - Optional: uncomment the two
allauth_*lines underapp'ssecrets:and the two under the top-levelsecrets:only after you create both secrets. DAIV then takes the OAuth client ID and secret from them and locks the fields under Configuration → Authentication. Leave them commented to set up OAuth login in the dashboard.
Deploy with Docker Compose
Docker Compose runs every service from one file, which suits development and smaller production setups. Before you start, install Docker with Compose, and check the requirements.
-
In the directory that will hold your
docker-compose.yml, create the egress CA for the sandbox. The sandbox reaches your Git platform only through a per-session proxy that adds the credentials, so the token never enters the sandbox, and the proxy signs with this CA: -
Make the key readable, because the sandbox service runs as UID 1001:
Bash -
Save the Compose file as
docker-compose.yml, and replace each annotated value with your own. Environment variables lists the options. -
Start the services:
Bash The command returns once every service is healthy. If one isn't,
docker compose logs <service>shows why. -
Put DAIV behind Nginx, so you can reach it over HTTPS.
-
Create the first admin by running
bootstrap_admin, replacingadmin@example.comwith the admin's email address:Bash -
Open
/accounts/login/and sign in with the one-time code DAIV emails to that address.
If you turn on OAuth login before anyone signs in, the first Git platform sign-in becomes the admin instead. The first admin explains both paths, and Accounts and roles covers adding users.
Compose file
| YAML | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
- Required: every service built from this anchor shares this directory. It holds the artifacts the agent publishes:
appwrites them for chat turns and serves them, andworkerwrites them for jobs and webhook runs. - Raises the open-file limit for every service built from this anchor. Each agent run holds many concurrent sockets (sandbox, LLM API, tracing, database, Redis, git over HTTPS), and Docker's default soft limit of 1024 open files fails under load with
[Errno 24] Too many open files. - Generate a Django secret key, a long random string such as the output of
openssl rand -hex 32. - Replace with your domain name, without the scheme (for example
daiv.example.com). Keepappand127.0.0.1for communication between services. - Include the full URL with the scheme (for example
https://daiv.example.com). Your GitLab sends webhooks to this URL, so it must be able to reach it. - Generate a random password for the database, such as the output of
openssl rand -hex 16. - Set to your GitLab instance URL (for example
https://gitlab.com). - The personal access token of DAIV's own GitLab user, with the
apiscope and the Maintainer role on your projects. Use a dedicated account: DAIV ignores comments written by the account its token belongs to. See Platform setup. - Generate a random webhook secret for GitLab webhook validation, such as the output of
openssl rand -hex 32. - An OpenRouter API key for LLM access. DAIV copies it into its database on the first start. To change it later, edit the provider under Configuration → Providers in the dashboard; see LLM providers.
- The SMTP server DAIV sends sign-in codes through. Without one, replace the three
EMAIL_*lines withEMAIL_BACKEND: django.core.mail.backends.console.EmailBackendand read the codes fromdocker compose logs app. - Generate a random API key for the sandbox service, such as the output of
openssl rand -hex 32. - Use the same password as in annotation 6.
- Redis 8 or later; see Requirements.
- Optional: uncomment the
volumes:block ofapp,workerandworker-interactiveto mount custom global skills that are available across all repositories.appreceives uploads from the dashboard, so it mounts the directory read-write; the workers run the agent, so they can mount it read-only. A service-levelvolumes:replaces the anchor's list instead of merging with it, so keep themedia-volumeline. - Scaling: increase
replicasto handle more concurrent agent runs (for examplereplicas: 3). A worker runs one task to completion before it claims the next, so concurrency is the number of worker containers. Each claims independently from the queues named in its command. - Replace
987with the group of the Docker socket, the output ofstat -c '%g' /var/run/docker.sock. The sandbox starts a container for each agent run through it. - Use the same API key as in annotation 12.
- The egress CA from step 1 of Deploy with Docker Compose.
Put DAIV behind Nginx
An Nginx reverse proxy gives your DAIV instance HTTPS access and domain routing. This page gives an Nginx configuration only; it has none for Apache, Traefik or other proxies.
DAIV needs HTTPS. Its session cookie, which also carries the CSRF token, is a secure cookie, so a browser keeps it only on https:// URLs and on localhost. Over plain http:// to any other address, sign-in fails with CSRF verification failed.
Before you start, install Nginx, and have an SSL certificate for your domain and a domain name that points to your server.
-
Save the Nginx configuration as
/etc/nginx/conf.d/daiv.conf. The path may vary by operating system. Replace each annotated value with your own. -
Restart Nginx:
Bash -
In a browser, open
https://daiv.example.com/accounts/login/, replacing the domain with yours. You see the DAIV login page.
Nginx configuration
- Set the internal IP of your DAIV instance. Use
localhostor127.0.0.1if Nginx runs on the same server. - Replace with your domain name (for example
daiv.example.com). - Update the SSL certificate path. The location varies by operating system.
- Update the SSL certificate key path. The location varies by operating system.
Next steps
- Platform setup connects DAIV to your GitLab or GitHub repositories.
- LLM providers configures your LLM providers and API keys.
- Repository configuration customizes DAIV's behavior per repository.
- Site configuration manages settings from the dashboard instead of environment variables.
- MCP tools connects the agent to external MCP servers.