The-Y CRM on your own server
One command, no config files. The installer generates all passwords itself, detects your server environment and sets everything up. You enter the license key afterwards, conveniently in the app.
- A Linux server (root/SSH access). Even ~2 CPU / 4 GB RAM / 40 GB SSD is enough to start.
- Docker & Docker Compose (step 1 shows how to install them if missing).
- A domain (e.g.
crm.your-company.com) whose DNS points to the server. Without a domain it only works in your own network (home network). - A license key: Paid (no limits) from the Founder welcome email or after purchase on the-y.at — or a free key (with the Free limits): sign up on crm.the-y.at → Settings → License → “Create free key”.
→ Which server do I need? — size recommendation (small/medium/large), ports & architecture in detail.
-
Install Docker (if not present yet)
Check with
docker version. If Docker is missing, install it with the official script:curl -fsSL https://get.docker.com | sh
On servers with a panel (Plesk/cPanel/Virtualmin) Docker is often already present or one-click installable.
-
Point the domain at the server
At your domain provider, set an A record for your domain (e.g.
crm.your-company.com) to your server's IP address. It can take a few minutes to propagate everywhere. -
Run the installer
A single command on the server (as root, or with a user in the
dockergroup):curl -fsSL https://the-y.at/install.sh -o install.sh && bash install.sh
The installer only asks for domain, admin email, (optional) password and company name — then does the rest: generates all passwords, sets up the database + first admin and automatically detects whether a web server is already running on your server. Depending on that, you continue in one of the three cases below.
Since 1.1.58 it also asks whether to set up the phone system as well — with “no” everything stays as before, and you can add it any time (phone system).
Depending on your server — one of these three cases
🟢 Fresh server
Nothing runs on port 80/443. The installer brings its own HTTPS (fetches a Let's Encrypt certificate automatically).
You don't need to do anything else — just open your domain in the browser. If the page briefly doesn't load: check DNS/ports 80+443.
🧩 Server with a panel
Virtualmin, Plesk or cPanel already occupy 80/443. The installer automatically switches to proxy mode (the app listens locally on 127.0.0.1:8090).
In the panel, add a reverse proxy / proxy pass for your domain, target:
http://127.0.0.1:8090
and enable HTTPS/Let's Encrypt for the domain in the panel. Done.
⚙️ Apache/Nginx only
You have your own web server without a panel. Here too the app runs on 127.0.0.1:8090 — add a reverse-proxy vhost (snippets below) and get a certificate with certbot.
Nginx vhost (Case C)
server {
server_name crm.your-company.com;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
}
}
# then: sudo certbot --nginx -d crm.your-company.com
Apache vhost (Case C)
<VirtualHost *:443>
ServerName crm.your-company.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8090/
ProxyPassReverse / http://127.0.0.1:8090/
RequestHeader set X-Forwarded-Proto "https"
# SSLEngine + certificate via certbot --apache
</VirtualHost>
Final step — sign in & enter the license
-
Open in the browser & sign in
Go to https://your-domain and sign in with the admin email and the password the installer showed at the end.
-
Enter the license key — in the app
The setup wizard asks for it on first launch, or you go to Settings → License and enter your key. No config file needed. Self-hosting requires a license key — Paid (no limits) or free (the same limits as Free in the cloud: 3 users, 1,000 outgoing messages and 200 outgoing call minutes per month, 2 simultaneous calls, 2 GB, 1 brand; no support). Without a key the instance runs fully for 14 days from the first start and is read-only after that, until you enter a key. To go from free to Paid later, just enter the Paid key — your data stays.
Updates arrive automatically: from 1.1.53 the installation updates itself at night with
update.sh – a backup is made first, the new version is checked, and if anything fails the previous version runs again. Installations from before 1.1.53 use Watchtower until you switch (about 5 minutes). A banner in the app reports new versions. You can also update yourself at any time:
cd ~/the-y-crm && bash update.shWithout
update.sh (installations from before 1.1.53): docker compose pull && docker compose up -d.
The app applies database changes itself on start; there is nothing else to do after an update.
For backups, regularly back up the Postgres and the MinIO volume — a simple database dump:
cd ~/the-y-crm && docker compose exec -T postgres \ pg_dump -U crm crm | gzip > crm-backup-$(date +%F).sql.gz
Phone system
The built-in phone system (Asterisk for calls, coturn for video meetings) is optional. Everything else runs without it.
Install it right away or add it later (from 1.1.58)
install.sh asks whether to set up the phone system as well — only on Linux servers running Docker directly (not Docker Desktop, not “rootless”), because voice runs in the host network. With yes, your telephony provider’s lines, extensions, voice menus, queues, voicemail, recordings and the phone in the browser work afterwards; the CRM generates the configuration itself. With no, everything stays as before. You can add it any time, also more than once – what exists stays:
cd ~/the-y-crm && curl -fsSL https://the-y.at/install.sh -o install.sh && bash install.sh --add-phone
- Port check: first the installer checks whether 5060/udp, 3478, 5038 and 8188 are free. If they’re taken, it suggests “no” and names them.
- Public IPv4 address: it asks for it and suggests your own address or the one your domain points to – no third-party service is asked. Behind NAT it also remembers your local address.
- Credentials:
AMI_SECRETandTURN_SECRETare generated randomly and only stored in the.env. - Announcements: it downloads the English standard prompts (Asterisk Core Sounds, 2.4 MB, with checksum) from downloads.asterisk.org. Without internet: download the file elsewhere, copy it to the server, then
PBX_ANSAGEN_DATEI=<file> bash telefonanlage.sh --ansagen. - Phone in the browser: with the built-in Caddy (case A) it adds
/asterisk-wsitself. Behind your own web server (case B/C), forward/asterisk-wsas a WebSocket tohttp://<PBX_DOCKER_GW>:8188/ws(value from the.env, usually172.17.0.1); the installer shows the lines with the right value. Without this forwarding, calls over lines and desk phones work, only the phone in the browser doesn’t. - Firewall: if the installer set up the firewall (hardening, ufw), it opens the phone system’s ports there; otherwise it names them (
bash telefonanlage.sh --ports). It never switches a firewall off. - Without questions:
PBX=1 PBX_PUBLIC_IP=203.0.113.10 bash install.sh(PBX=0= no; behind NAT alsoPBX_LOCAL_IP=<your address>).
Ports (incoming — also in your provider’s firewall or as port forwarding on the router if the server is behind NAT): 5060/udp (SIP), 10000–20000/udp (voice), 3478/udp + 3478/tcp (STUN/TURN for video meetings), 49152–49252/udp (TURN), 443/tcp (phone in the browser, open for the app anyway). Never open from outside: 5038/tcp (AMI) and 8188/tcp (Asterisk HTTP).
# Nginx — in the HTTPS vhost of your domain, before “location /”
location = /asterisk-ws {
proxy_pass http://172.17.0.1:8188/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# Apache — a2enmod proxy_wstunnel; before “ProxyPass /” ProxyPass /asterisk-ws ws://172.17.0.1:8188/ws ProxyPassReverse /asterisk-ws ws://172.17.0.1:8188/ws
Updates: after every update with a new worker, update.sh takes over the phone system’s base files; the running system reloads them, calls keep going. Your own parts belong in pbx/asterisk/include/. New Asterisk and coturn versions come with bash install.sh --add-phone. Check the state: bash telefonanlage.sh --pruefen.
Switching it off: docker compose rm -sf asterisk coturn, in the .env remove :docker-compose.pbx.yml from COMPOSE_FILE and set PBX=0, then docker compose up -d. The files under pbx/ stay.
SELF-HOSTING.md (German) → Telefonie. --add-phone leaves phone systems set up by hand (before 1.1.58) untouched; the section below still applies to them.What applies from 1.1.46
If you use the built-in phone system, the following applies from version 1.1.46. The version itself arrives with the automatic updates; the parts under “Not in the image” you take over once yourself.
- Simultaneous calls: without a paid key your installation counts as Free: at most 2 simultaneous calls (over your lines and over WhatsApp, incoming and outgoing). You can change this with
PBX_CONCURRENT_FREEin the.env(a number,-1= no limit). With a paid key there is no plan limit. - Limit for the whole server:
PBX_MAX_CONCURRENT(default 80,0= no server limit). App and worker read the same.envand must see the same number. A new value only takes effect when you recreate both:docker compose up -d app worker. Without theulimitsbelow, set 55 at most. - Not in the image: automatic updates only replace the images. Take these parts over from the release once:
pbx/asterisk/pjsip.conf(block[webrtc-endpoint]),pbx/asterisk/modules.conf(new; current version: the-y.at/pbx/asterisk/modules.conf.txt),pbx/asterisk/rtp.conf(RTP 10000–20000, also open it in the firewall) and, in the compose serviceasterisk, theulimitsand the mount formodules.conf:
asterisk:
# … existing entries stay …
ulimits:
nofile:
soft: 65536
hard: 65536
volumes:
# … existing entries stay …
- ./pbx/asterisk/modules.conf:/etc/asterisk/modules.conf:ro
Then recreate the container once, best at a quiet time (running calls drop):
docker compose up -d asterisk
Announcements and notice before recordings: since 1.1.51 there is no computer voice any more. Each account records its announcements itself (in the browser) or uploads them as a file. Without your own recording the system plays the standard English announcement where there is one; before a recording, callers without your own notice only hear a beep (the app then shows a clear warning). More: Notice before recordings · Release notes 1.1.51 · Release notes 1.1.46 · Simultaneous calls.
Without a domain in your home network (from 1.1.58)
On a home server (e.g. CasaOS/ZimaOS, TrueNAS) without a domain and without a certificate, the CRM also works over http://<server IP>. This is meant for your own network only: the connection is not encrypted. For access from outside, use a domain with https.
With install.sh (from 1.1.59): when the installer asks for your domain, just enter the server's IP address (e.g. 192.168.1.50). The installer then writes the values below itself and tells you so.
By hand: in the .env, enter the address with http:// instead of the domain (replace the example IP), then run docker compose up -d in the installation folder:
CADDY_DOMAIN=http://192.168.1.50 # with “http://” in front, Caddy fetches no certificate BASE_DOMAIN=192.168.1.50 # just the address, without http:// and without a path APP_URL=http://192.168.1.50 # starts with http:// → signing in over http works, links point to this address PUBLIC_BASE_URL=http://192.168.1.50
If an app store opens the app directly on a port (without Caddy), that address including the port goes into APP_URL, e.g. APP_URL=http://192.168.1.50:3000. The sign-in page then shows the notice that the connection in your home network is not encrypted.
Links and images in emails (from 1.1.59): the app builds links in emails and notifications (invitations, calendar file, unsubscribe and survey links, report) from APP_URL as soon as it starts with http://. So set APP_URL exactly the way you open the app in the browser (with the port, if needed). Such links only work inside your own network. The app embeds the logo and images from email templates in the email so recipients can see them.
Interface language (from 1.1.59)
Anyone opening the app for the first time (no language chosen yet) sees it in their browser language – German, English or Turkish, otherwise German. To fix it, set it in the .env, then run docker compose up -d app in the installation folder:
DEFAULT_LOCALE=en # de, en or tr
Order: DEFAULT_LOCALE > browser > German. The language picker in the app and the language in your own profile always come first.
“Translate” in the inbox
Without a translation service (and without your own AI or DeepL key as translator), the inbox hides the “Translate” buttons (from 1.1.59). Setting up the service: Self-hosting operations → Translation.
Stuck somewhere?
Just write to us — reply to your Founder welcome email or reach out via the-y.at. We'll help you set it up.