Don’t want to read all this?
Just drop a link to https://xedant.com/agents/crm/install.md or to https://xedant.com/llms.txt into any AI chat (Claude, ChatGPT, etc.) and ask it to generate the config files and commands. It will read the docs, ask you a few questions about your setup, and hand you a ready-to-use configuration. Save time — let the model do the reading for you.
You can also reach me on Telegram — I’m always glad to help. And that’s not just politeness — I genuinely enjoy talking to like-minded people, especially if you love coding as much as I do.
CRM Agent is a CRM on your server in which an AI agent keeps the data while you watch and give commands. It deploys as a single Docker container, and a separate database is optional: by default an embedded one is already inside. Next to it, optionally, sits your Xedant Agent — then you can run the CRM by talking in chat, and without it everything except the chat with the agent works as usual.
What you will need
- A server or computer with Docker — Windows, Linux or macOS all work, and an inexpensive server is enough for permanent use.
- One free port for the interface (3990 in the examples below). A separate database is not needed: if you do not set a connection string, the product starts an embedded database by itself.
- The login and password for the first sign-in. You can set them in advance with the
CRM_APP_LOGINandCRM_APP_PASSWORDvariables; if you leave them unset, the administrator is the first person who registers in the interface. - A management key for the agent (
CRM_APP_HASH) — if you want your AI agent to change the data itself instead of you doing it by hand. The variable holds not the key itself but its fingerprint: you invent the key once and give it to the agent, and the product remembers the fingerprint and recognises the key by it. Until the fingerprint is set, changes are refused, while viewing in the interface keeps working. - Optionally, the address and key of your Xedant Agent. Without them everything works except the chat with the AI agent inside the product.
Installing with Docker Compose
Create a compose.yml file with this content. There is one service here: it needs no separate database, so the whole product fits into a single container. There are two persistent folders — one holds the data, the other the descriptions of your scripts.
services:
crmagent:
image: xedant/crm-agent:min-latest
container_name: crmagent
ports:
- "3990:80"
volumes:
- crmagent-data:/data
# The scripts yml config tree (apps/crm) — a named volume so API-edited
# definitions survive container recreation (seeded on first boot).
- crmagent-config:/project/apps/crm
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:80
- CRM_APP_DATA=/data
# CRM_APP_BASE_PATH=/crm # serve from a first-level subfolder (see ../subfolder-hosting.md)
# The image ships the minimal idle-memory runtime posture (workstation GC,
# non-concurrent GC, 1 MB GC regions, W^X and diagnostics IPC off, glibc arenas
# capped — see the app Dockerfile and dev/idle-memory.md). Compose entries here
# override the image, e.g. put Server GC back on for a busy instance:
# - DOTNET_gcServer=1
restart: unless-stopped
sysctls:
fs.inotify.max_user_watches: "524288"
fs.inotify.max_user_instances: "512"
volumes:
crmagent-data:
crmagent-config:
Line by line, what this says and why:
image— the English build of the product with an English interface and the name “CRM Agent”.ports— the address the interface opens on:3990outside,80inside the container. The outside number can be any free port.volumes— two persistent folders./dataholds the database and all the data, while/project/apps/crmholds the descriptions of your scripts and their settings. Without these folders everything disappears when the container is recreated, and the script descriptions would have to be created again.CRM_APP_DATA— that same persistent folder inside the container, where the product keeps everything of its own; it is the folder mounted as the volume above.CRM_APP_BASE_PATH— the commented line for hosting in a subfolder of the site: uncomment it if you want to open the CRM at an address likeyour-site/crm.- The comments about memory and the line
# - DOTNET_gcServer=1— a hint for a busy instance: the image already carries the low-consumption settings, and this line can be uncommented if many people work at the same time (more in the “Saving resources while idle” section below). restart— restarting after a server reboot, so the CRM comes back up by itself.sysctls— two Linux kernel settings raising the file-watching limit: without them the product notices changes in its folders worse at scale.
Then start it:
docker compose up -d
The interface appears on port 3990: http://<server-address>:3990.
Running with one command
If you would rather not keep a settings file, the same thing is done with one docker run command:
docker run -d \
--name crmagent \
-p 3990:80 \
-v crmagent-data:/data \
-v crmagent-config:/project/apps/crm \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ASPNETCORE_URLS=http://+:80 \
-e CRM_APP_DATA=/data \
--sysctl fs.inotify.max_user_watches=524288 \
--sysctl fs.inotify.max_user_instances=512 \
--restart unless-stopped \
xedant/crm-agent:min-latest
The remaining variables are convenient to pass in a file with the --env-file .env flag — easier to change them without rewriting the long command.
Saving resources while idle
While nobody is working in the CRM, the product spends neither processor time nor extra memory. It has no permanent background checks: it wakes on an event — a request came in, a letter arrived, a scheduled deadline is due. A measurement on a server showed 0.0–0.3 % of the processor at rest; the previous version spent 0.77 % continuously.
Memory is returned to the server as well. If nothing happens in the CRM for a full 15 minutes, the accumulated data is released. The window is set by the CRM_APP_IDLE_TRIM_MINUTES variable: 15 minutes by default, 0 turns it off. You can see what the product is occupying right now at the service address /api/health — it is protected by sign-in, like everything else.
The image already carries the low-consumption memory settings, so nothing needs tuning separately. If the instance is a busy one and you want headroom at peaks, compose.yml has a commented-out line # - DOTNET_gcServer=1: uncomment it.
Environment variables
Settings are passed through environment variables — you write them once into compose.yml and change them without touching the code. Almost all names start with CRM_APP_; for the chat with the agent, the names are the familiar ones — without the prefix.
| Variable | What it sets |
CRM_APP_DB | database choice. Not set — the product runs on the embedded database in the file /data/data.db; set to a connection string — it runs on PostgreSQL |
CRM_APP_DATA | persistent data folder: the database, sign-in keys, brand settings. Default /data |
CRM_APP_BRAND | build brand: xedant is the English build (the default), pastukhov the Russian one with the Russian product name and interface |
CRM_APP_BASE_PATH | hosting in a subfolder of the site, for example /crm; not set — runs at the domain root |
CRM_APP_HASH | fingerprint of the management key: by it your AI agent changes the data over the API (the management addresses it calls). Stored as an irreversible SHA-256 sum; not set — changes are refused, while viewing keeps working |
CRM_APP_LOGIN, CRM_APP_PASSWORD | the administrator account created at startup. If you leave them unset, the administrator is the first person who registers in the interface |
CRM_APP_INTAKE_ALLOW_ORIGIN | the list of sites intake requests are accepted from. Not set — requests are accepted from any site; set — only from the listed ones |
CRM_APP_COMPRESSION | server response compression. Enabled by default, =0 disables it |
CRM_APP_JOB_KEEP_DAYS | how many days to keep finished background jobs. Default 30 |
CRM_APP_IDLE_TRIM_MINUTES | how many minutes of complete idleness to wait before releasing the accumulated data and returning memory to the server. Default 15, 0 disables it |
CRM_APP_SEARCH_INDEX_FOLDER | the folder holding the search list for product-wide search. By default next to the data, in the search-index folder |
CRM_APP_BOOT_FULL | =1 — a full check of the database layout at startup. Useful for diagnostics, normally not needed |
CRM_APP_SSL | =auto — the product issues and signs an HTTPS certificate itself for access at a direct address with a port. If your proxy with its own certificate stands in front of the product, this variable is not needed |
AGENT_API_URL, AGENT_API_KEY | address and key of your Xedant Agent: with them the chat with the AI agent appears inside the CRM. Not set — the chat is hidden, everything else works |
The .env file next to the project
Some settings and all secrets are more convenient to keep not in environment variables but in a .env file at the root of the project folder — inside the container that is /project/.env. Its entries are simple, one per line: CRM_APP_HASH=your-fingerprint; lines starting with # serve as comments. For the container to see the file, mount it as a volume, and if it lives somewhere else, point to it with the CRM_APP_ENV_FILE variable.
The file is read at startup and then re-read on the fly: edits in it apply without a restart and are visible to every program the product launches. Settings that are read only at startup (the database, the brand, the product address, and the address and key of the agent) take effect after a restart. If the same setting is given both in the file and in the container environment, the value from the file wins. The product adds the file to the git ignore list itself, so secrets never land in the repository.
Do not confuse this file with the one passed when the container starts with the --env-file flag: there Docker reads the file on your computer and simply sets the variables, while the .env next to the project is read and re-read by the product itself.
Hosting in a subfolder
The product can be served not from the domain root but from a subfolder — for example, https://your-site/crm. There are two ways, and they do not mix.
- Just name the folder. Set
CRM_APP_BASE_PATH=/crm— the product strips the prefix itself and redirects the old addresses to the new form. The domain-check service addresses (/.well-known/) stay at the root. - Hand the folder to your proxy. In this case nothing needs changing inside the container: the proxy tells the product the prefix in the
X-Forwarded-Prefixheader. This is more convenient when you already have nginx or a similar server configured.
In the second case, do not forget the live updates: they need the WebSocket upgrade headers. Two update channels work inside — the general one and the one for the chat with the agent — and both need this upgrade, otherwise the updates quietly turn into rare polling.
# WebSocket upgrade (in the common http {} block)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name host.tld;
client_max_body_size 100m; # file uploads and pasted pictures
location /crm/ {
proxy_pass http://crmagent:80/; # the trailing slash strips the subfolder prefix
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Prefix /crm;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # live updates: /appHub and /agentChatHub
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s; # long chat answers
proxy_send_timeout 3600s;
proxy_buffering off; # streaming answers
}
}
Both ways, and the isolation check between folders, are described in the install/subfolder-hosting.md file next to the product.
First sign-in
Open the interface in a browser. If you set CRM_APP_LOGIN and CRM_APP_PASSWORD, sign in with those; otherwise the first person to register becomes the administrator, after which open registration closes — an outsider can no longer get into your installation. A sign-in session lives 90 days, so you will not have to log in every day. If the product stands behind your organization’s single sign-on (when an external system checks the login), users are created automatically on first visit.
Installing through Xedant MultiAgent
If you have several servers, it is more convenient to install the product from Xedant MultiAgent: it raises the container itself, provides a permanent address and HTTPS, and connects the database. CRM Agent is in the product catalogue (port 6080). MultiAgent passes the product the variables CRM_APP_SSL, CRM_APP_DB and the management key fingerprint CRM_APP_HASH, while the linked Xedant Agent receives the product’s address and key in the variables CRM_APP_URL and CRM_APP_KEY — that is how it reaches the CRM. More about MultiAgent itself is on the MultiAgent page.
License and updates
The trial period is 30 days free, with no limits inside. Prices are the same as the neighboring products: personal
Next, take a look at Getting Started — a short tour of how the product is built and where to begin.