Self-Hosting Guide - Relaticle  

    [Developer guides](https://relaticle.com/developers)

 Self-Hosting Guide 
====================

Get Docker Compose, Coolify and Dokploy deployment steps for Relaticle, with PostgreSQL, Redis and Ollama setup.

 Updated August 30, 2026

 Deploy Relaticle on your own infrastructure with Docker or manually.

---

[](#quick-start "Permalink")Quick Start
---------------------------------------

Get Relaticle running in 5 steps:

1. Download the compose file:

```
curl -o compose.yml https://raw.githubusercontent.com/Relaticle/relaticle/main/compose.yml

```

2. Generate an application key:

```
echo "APP_KEY=base64:$(openssl rand -base64 32)"

```

3. Create a `.env` file with your settings:

```
APP_KEY=base64:your-generated-key-here
DB_PASSWORD=your-secure-database-password
APP_URL=https://crm.example.com

```

4. Start the containers:

```
docker compose up -d

```

5. Create your admin account:

```
docker compose exec app php artisan make:filament-user

```

Your CRM is now available at `{APP_URL}/app`.

---

[](#requirements "Permalink")Requirements
-----------------------------------------

| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 2 GB | 4 GB |
| CPU | 1 core | 2 cores |
| Disk | 10 GB | 20 GB+ |
| Docker | 20.10+ | Latest |
| Docker Compose | v2.0+ | Latest |

---

[](#environment-variables "Permalink")Environment Variables
-----------------------------------------------------------

### [](#required "Permalink")Required

These must be set or the containers will refuse to start.

| Variable | Description |
|---|---|
| `APP_KEY` | Encryption key. Generate with `openssl rand -base64 32`, then prefix with `base64:`. |
| `DB_PASSWORD` | PostgreSQL password. Use a strong random value. |

### [](#application "Permalink")Application

| Variable | Default | Description |
|---|---|---|
| `APP_NAME` | `Relaticle` | Displayed in the browser tab and emails. |
| `APP_ENV` | `production` | Set to `production` for self-hosting. |
| `APP_DEBUG` | `false` | Set to `true` only for debugging. Never in production. |
| `APP_TIMEZONE` | `UTC` | Application timezone. |
| `APP_URL` | `http://localhost` | Full URL where Relaticle is accessible. Include the scheme. |
| `APP_PORT` | `80` | Host port the app container binds to. |
| `APP_PANEL_DOMAIN` | (empty) | Set for subdomain routing (e.g., `app.example.com`). Leave empty for path mode (`/app`). |
| `PASSKEYS_USER_HANDLE_SECRET` | `APP_KEY` | Derives the opaque WebAuthn user handle stored on each authenticator. Set it to its own random value (`openssl rand -base64 32`) before anyone registers a passkey, so rotating `APP_KEY` later does not change every user's handle. |
| `REQUIRE_EMAIL_VERIFICATION` | `true` | When `false`, users sign in without verifying their email. That is useful for self-hosters who haven't configured SMTP yet. The admin you create via `make:filament-user` is auto-verified regardless, so the default of `true` is safe for fresh Docker installs. Only set to `false` if your panel is on a private network: with verification disabled, anyone who can reach the sign-in page can create a working account. |
| `LOG_CHANNEL` | `stderr` | Where logs go. `stderr` is recommended for Docker. |
| `LOG_LEVEL` | `warning` | Minimum log level. Use `debug` for troubleshooting. |

### [](#mail "Permalink")Mail

| Variable | Default | Description |
|---|---|---|
| `MAIL_MAILER` | `log` | Mail driver: `smtp`, `ses`, `mailgun`, `postmark`, or `log`. |
| `MAIL_HOST` | (empty) | SMTP host (e.g., `smtp.mailgun.org`). |
| `MAIL_PORT` | `587` | SMTP port. |
| `MAIL_USERNAME` | (empty) | SMTP username. |
| `MAIL_PASSWORD` | (empty) | SMTP password. |
| `MAIL_ENCRYPTION` | `tls` | `tls` or `ssl`. |
| `MAIL_FROM_ADDRESS` | `hello@example.com` | Sender email address. |
| `MAIL_FROM_NAME` | `Relaticle` | Sender display name. |

### [](#database-and-redis "Permalink")Database and Redis

These are pre-configured in `compose.yml` and generally don't need changing.

| Variable | Default | Description |
|---|---|---|
| `DB_DATABASE` | `relaticle` | PostgreSQL database name. |
| `DB_USERNAME` | `relaticle` | PostgreSQL username. |
| `REDIS_PASSWORD` | `null` | Redis password. Leave as `null` for no auth. |

### [](#optional "Permalink")Optional

| Variable | Description |
|---|---|
| `GOOGLE_CLIENT_ID` | Google OAuth client ID for social login. |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret. |
| `GOOGLE_REDIRECT_URI` | Absolute callback URL (`https://your-host/auth/callback/google`), or a relative path (`/auth/callback/google`) to derive the host from the request. |
| `MICROSOFT_CLIENT_ID` | Microsoft Entra ID client id. The Microsoft button appears only when this is set; register `https://your-host/auth/callback/microsoft` as the redirect URI. |
| `MICROSOFT_CLIENT_SECRET` | Microsoft Entra ID client secret. |
| `SENTRY_LARAVEL_DSN` | Sentry DSN for error tracking. |
| `FATHOM_ANALYTICS_SITE_ID` | Fathom Analytics site ID. |

### [](#ai-assistant "Permalink")AI Assistant

The AI assistant works with cloud providers, a self-hosted server, or both. Models appear in the chat model picker only when their provider is configured. With none configured, the assistant cannot answer.

There are two ways to run a self-hosted model, and you can use either or both:

- **[Ollama](https://ollama.com)** (`OLLAMA_MODEL`): the simplest path, with the best tool-calling support.
- **Any OpenAI-compatible endpoint** (`SELF_HOSTED_AI_*`): point at [vLLM](https://docs.vllm.ai) (best for many concurrent users), LM Studio, LocalAI, or a hosted gateway. Exposes multiple models at once.

| Variable | Default | Description |
|---|---|---|
| `ANTHROPIC_API_KEY` | (empty) | Enables the Claude models in the chat picker. |
| `OPENAI_API_KEY` | (empty) | Enables the GPT models in the chat picker. |
| `OLLAMA_BASE_URL` | `http://localhost:11434` | URL of your Ollama server. From inside a Docker container, `localhost` is the container itself. Use `http://host.docker.internal:11434` (or your host's LAN IP) to reach Ollama running on the host. |
| `OLLAMA_MODEL` | (empty) | Ollama model tag (e.g. `qwen3:14b`). Setting this adds the model to the chat picker. |
| `SELF_HOSTED_AI_URL` | (empty) | Base URL of an OpenAI-compatible endpoint (e.g. `http://localhost:8000/v1`). |
| `SELF_HOSTED_AI_KEY` | (empty) | API key for that endpoint, if it requires one (blank for most local servers). |
| `SELF_HOSTED_AI_MODELS` | (empty) | Comma-separated model tags to expose (e.g. `qwen3:14b,llama3.1:70b`). Each becomes a picker entry. |

**Choosing a model.** The assistant calls over 30 CRM tools (search, create, update, delete). Only models with strong tool-calling support work reliably. `qwen3` and `llama3.1:70b`-class models are good starting points. Small models tend to claim an action succeeded without actually invoking the tool. Verify a model before trusting it with `php artisan chat:models --probe=`, which lists the registry and smoke-tests tool-calling against its endpoint (self-hosted models only; cloud models run through the vendor SDK and are declined).

**Known limitations with self-hosted models:**

- Cloud providers enforce one-write-proposal-at-a-time at the API level; self-hosted models have no equivalent switch, so this is enforced by prompt instructions only. Every write still requires your explicit approval before anything is saved.
- Each response must complete within 120 seconds. If a slow model exceeds it, the turn stops cleanly. Your message stays in the conversation with a "didn't respond within the time limit" note and nothing is silently lost, so you can just retry. To avoid the limit, use a smaller/faster model or a GPU; "thinking" models like `qwen3` reason before answering and reach it sooner, so a non-thinking model or a shorter prompt helps for interactive use.

### [](#feature-flags "Permalink")Feature Flags

Toggle features on or off. All are enabled by default unless noted. Useful for forks and custom deployments that want to disable specific functionality without modifying code.

| Variable | Default | Description |
|---|---|---|
| `RELATICLE_FEATURE_ONBOARD_SEED` | `true` | Seed demo data (sample companies, contacts, tasks) when a new workspace is created. Set to `false` to start with an empty workspace. |
| `RELATICLE_FEATURE_SOCIAL_AUTH` | `true` | Enable Google and Microsoft social login. Set to `false` to use only email, password, and passkey sign-in. |
| `RELATICLE_FEATURE_DOCUMENTATION` | `true` | Enable the `/developers` and `/help` documentation module. Set to `false` to remove documentation routes and navigation links. |
| `RELATICLE_FEATURE_SIGNUP_CHALLENGE` | `false` | Ask new sign-ups to pass a Cloudflare Turnstile check. Stays hidden for normal browsers and shows a checkbox only when Cloudflare needs one. Requires `TURNSTILE_SITE_KEY` and `TURNSTILE_SECRET_KEY`. |

---

[](#architecture "Permalink")Architecture
-----------------------------------------

The Docker setup runs 5 containers:

| Container | Image | Purpose |
|---|---|---|
| **app** | `ghcr.io/relaticle/relaticle:latest` | Web server (nginx + PHP-FPM) on port 8080. Runs migrations automatically on startup. |
| **horizon** | `ghcr.io/relaticle/relaticle:latest` | Queue worker powered by Laravel Horizon. Processes background jobs. |
| **scheduler** | `ghcr.io/relaticle/relaticle:latest` | Runs `schedule:work` for recurring tasks (e.g., cleanup, notifications). |
| **postgres** | `postgres:17-alpine` | PostgreSQL 17 database. |
| **redis** | `redis:7-alpine` | Cache, sessions, and queue backend. Runs with append-only persistence. |

### [](#volumes "Permalink")Volumes

| Volume | Purpose |
|---|---|
| `postgres` | Database files. Back this up. |
| `redis` | Redis persistence data. |
| `storage` | Uploaded files and application storage. Back this up. |

### [](#networking "Permalink")Networking

The app container listens on port 8080 internally and maps to `APP_PORT` (default 80) on the host. All containers communicate through Docker's internal network. Only the app container exposes a port to the host.

---

[](#creating-your-admin-account "Permalink")Creating Your Admin Account
-----------------------------------------------------------------------

After starting the containers, create your first admin user:

```
docker compose exec app php artisan make:filament-user

```

When prompted to pick a panel, choose `app`, then enter a name, email, and password. Once created, access the CRM panel at `{APP_URL}/app`. (To create the instance-wide system administrator instead, see the next section.)

**Note**: By default the CRM panel is available at the `/app` path. To use subdomain routing instead (e.g., `app.example.com`), set the `APP_PANEL_DOMAIN` environment variable.

### [](#system-administrator-account "Permalink")System Administrator Account

The `sysadmin` panel at `{APP_URL}/sysadmin` is a separate, instance-wide admin surface for managing every workspace and user on your installation. To create a system administrator, you can either pick `sysadmin` from the `make:filament-user` panel prompt, or use the dedicated command:

```
docker compose exec app php artisan sysadmin:create

```

---

[](#reverse-proxy-and-ssl "Permalink")Reverse Proxy and SSL
-----------------------------------------------------------

The app container serves HTTP on port 8080 internally. Place a reverse proxy in front to handle SSL termination and route traffic to the container.

SSL is not optional if you want passkeys: browsers run WebAuthn over HTTPS only, and a passkey is bound to the host it was registered on. That host is `APP_PANEL_DOMAIN` when subdomain routing is on, and the `APP_URL` host otherwise, so serve the panel from one hostname and keep it stable.

### [](#nginx "Permalink")Nginx

```
server {
    listen 443 ssl http2;
    server_name crm.example.com;

    ssl_certificate /etc/letsencrypt/live/crm.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/crm.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:80;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name crm.example.com;
    return 301 https://$server_name$request_uri;
}

```

### [](#caddy "Permalink")Caddy

```
crm.example.com {
    reverse_proxy 127.0.0.1:80
}

```

Caddy handles SSL certificates automatically via Let's Encrypt.

### [](#traefik "Permalink")Traefik

Add these labels to the `app` service in your `compose.yml`:

```
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.relaticle.rule=Host(`crm.example.com`)"
  - "traefik.http.routers.relaticle.entrypoints=websecure"
  - "traefik.http.routers.relaticle.tls.certresolver=letsencrypt"
  - "traefik.http.services.relaticle.loadbalancer.server.port=8080"

```

**Note**: When using a reverse proxy, set `APP_URL` to your public HTTPS URL (e.g., `https://crm.example.com`). The app trusts `X-Forwarded-*` headers from RFC1918 private networks, loopback, and IPv6 ULA/link-local, which covers Coolify/Dokploy/Traefik on a Docker network and reverse proxies on the host. Headers from public IPs are rejected, preventing spoofing.

---

[](#deploying-on-dokploy "Permalink")Deploying on Dokploy
---------------------------------------------------------

[Dokploy](https://dokploy.com/) is an open-source deployment platform. Here's how to deploy Relaticle on it.

### [](#1-create-a-project "Permalink")1. Create a Project

In the Dokploy dashboard, click **Create Project** and give it a name (e.g., "Relaticle").

### [](#2-add-a-compose-service "Permalink")2. Add a Compose Service

1. Inside your project, click **Create Service** and select **Compose**
2. Set the source to **Raw** and paste the contents of the [compose.yml](https://raw.githubusercontent.com/Relaticle/relaticle/main/compose.yml)
3. Click **Create**

### [](#3-set-environment-variables "Permalink")3. Set Environment Variables

In the service's **Environment** tab, add the required variables:

```
APP_KEY=base64:your-generated-key-here
DB_PASSWORD=your-secure-database-password
APP_URL=https://crm.yourdomain.com
APP_PORT=8080

```

Generate your `APP_KEY` with:

```
echo "base64:$(openssl rand -base64 32)"

```

**Note**: Set `APP_PORT=8080` so the container maps port 8080:8080, avoiding conflicts with Dokploy's own port 80.

### [](#4-deploy "Permalink")4. Deploy

Click **Deploy**. Dokploy will pull the images and start all 5 containers. Wait for health checks to pass.

### [](#5-configure-domain "Permalink")5. Configure Domain

1. Go to the **Domains** tab of the `app` service
2. Add your domain (e.g., `crm.yourdomain.com`)
3. Set the container port to `8080`
4. Enable HTTPS (Dokploy handles Let's Encrypt automatically)

### [](#6-create-admin-user "Permalink")6. Create Admin User

Open the Dokploy terminal for the `app` container and run:

```
php artisan make:filament-user

```

Your Relaticle instance is now live at `https://crm.yourdomain.com/app`.

---

[](#deploying-on-coolify "Permalink")Deploying on Coolify
---------------------------------------------------------

[Coolify](https://coolify.io/) is an open-source, self-hostable platform for deploying applications.

### [](#1-create-a-new-project "Permalink")1. Create a New Project

In the Coolify dashboard, click **New Project** and give it a name.

### [](#2-add-a-docker-compose-resource "Permalink")2. Add a Docker Compose Resource

1. Click **Add Resource** in your project
2. Select **Docker Compose**
3. Choose **Empty** as the source, then paste the contents of the [compose.yml](https://raw.githubusercontent.com/Relaticle/relaticle/main/compose.yml)

### [](#3-configure-environment-variables "Permalink")3. Configure Environment Variables

In the resource's **Environment Variables** section, add:

```
APP_KEY=base64:your-generated-key-here
DB_PASSWORD=your-secure-database-password
APP_URL=https://crm.yourdomain.com

```

### [](#4-set-up-domain "Permalink")4. Set Up Domain

1. Go to the app service's **Settings**
2. Set your domain (e.g., `crm.yourdomain.com`)
3. Coolify will automatically provision an SSL certificate

### [](#5-deploy "Permalink")5. Deploy

Click **Deploy**. Coolify will pull the images, create containers, and start the services.

### [](#6-create-admin-user-1 "Permalink")6. Create Admin User

Use Coolify's **Terminal** feature to run in the `app` container:

```
php artisan make:filament-user

```

Access your CRM at `https://crm.yourdomain.com/app`.

---

[](#upgrading "Permalink")Upgrading
-----------------------------------

### [](#1-back-up-your-data "Permalink")1. Back Up Your Data

Always back up before upgrading. See the Backup and Restore section below.

### [](#2-pull-the-latest-images "Permalink")2. Pull the Latest Images

```
docker compose pull

```

### [](#3-restart-the-containers "Permalink")3. Restart the Containers

```
docker compose up -d

```

Database migrations run automatically on startup. The app container will apply any pending migrations before serving traffic.

**Note**: Check the [release notes](https://github.com/Relaticle/relaticle/releases) before upgrading. Breaking changes will be documented there.

---

[](#backup-and-restore "Permalink")Backup and Restore
-----------------------------------------------------

### [](#database-backup "Permalink")Database Backup

```
docker compose exec postgres pg_dump -U relaticle relaticle > backup-$(date +%Y%m%d).sql

```

### [](#database-restore "Permalink")Database Restore

```
docker compose exec -T postgres psql -U relaticle relaticle  /backups/relaticle-$(date +\%Y\%m\%d).sql.gz

```

---

[](#manual-deployment "Permalink")Manual Deployment
---------------------------------------------------

If you prefer not to use Docker, you can deploy Relaticle directly on a server.

### [](#requirements-1 "Permalink")Requirements

- PHP 8.5+ with extensions: pdo\_pgsql, gd, bcmath, mbstring, xml, redis
- PostgreSQL 17+
- Redis 7+
- Node.js 22+
- Composer 2+
- Nginx or Apache
- Supervisor (for queue workers)

### [](#installation "Permalink")Installation

1. Clone the repository:

```
git clone https://github.com/Relaticle/relaticle.git /var/www/relaticle
cd /var/www/relaticle

```

2. Install dependencies:

```
composer install --no-dev --optimize-autoloader
pnpm install --frozen-lockfile && pnpm run build

```

3. Configure the environment:

```
cp .env.example .env
php artisan key:generate

```

Edit `.env` with your database credentials, mail settings, and `APP_URL`.

4. Set up the database:

```
php artisan migrate --force
php artisan storage:link

```

5. Set permissions:

```
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache

```

6. Create your admin user:

```
php artisan make:filament-user

```

### [](#nginx-configuration "Permalink")Nginx Configuration

```
server {
    listen 80;
    server_name crm.example.com;
    root /var/www/relaticle/public;

    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.5-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

```

### [](#supervisor-for-horizon "Permalink")Supervisor for Horizon

Create `/etc/supervisor/conf.d/relaticle-horizon.conf`:

```
[program:relaticle-horizon]
process_name=%(program_name)s
command=php /var/www/relaticle/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/relaticle/storage/logs/horizon.log
stopwaitsecs=3600

```

Then start it:

```
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start relaticle-horizon

```

### [](#scheduler-cron "Permalink")Scheduler Cron

Add to `www-data`'s crontab:

```
* * * * * cd /var/www/relaticle && php artisan schedule:run >> /dev/null 2>&1

```

---

[](#troubleshooting "Permalink")Troubleshooting
-----------------------------------------------

### [](#appkey-is-required-error-on-startup "Permalink")"APP\_KEY is required" error on startup

The `APP_KEY` environment variable is not set. Generate one:

```
echo "base64:$(openssl rand -base64 32)"

```

Add it to your `.env` file and restart:

```
docker compose up -d

```

### [](#container-restart-loops "Permalink")Container restart loops

Check the logs:

```
docker compose logs app --tail 50

```

Common causes:

- Missing required environment variables (`APP_KEY`, `DB_PASSWORD`)
- PostgreSQL not ready yet (the app waits for a health check, but custom configs may skip this)
- Insufficient memory (need at least 2 GB)

### [](#500-errors-after-deployment "Permalink")500 errors after deployment

1. Check `APP_DEBUG=true` temporarily to see the full error
2. View logs: `docker compose logs app --tail 100`
3. Ensure `APP_URL` matches your actual domain (including scheme)
4. Run `docker compose exec app php artisan optimize:clear` to clear all caches

### [](#email-not-sending "Permalink")Email not sending

1. Verify `MAIL_MAILER` is set to `smtp` (not `log`)
2. Check your SMTP credentials are correct
3. Test with: `docker compose exec app php artisan tinker --execute "Mail::raw('Test', fn(\$m) => \$m->to('you@example.com')->subject('Test'))"`
4. Check logs: `docker compose logs app | grep -i mail`

### [](#horizon-not-processing-jobs "Permalink")Horizon not processing jobs

1. Check Horizon status: `docker compose exec app php artisan horizon:status`
2. Verify the horizon container is running: `docker compose ps`
3. Check horizon logs: `docker compose logs horizon --tail 50`
4. Restart Horizon: `docker compose restart horizon`

### [](#permission-errors-on-storage "Permalink")Permission errors on storage

```
docker compose exec app chown -R www-data:www-data /var/www/html/storage
docker compose exec app chmod -R 775 /var/www/html/storage

```

### [](#database-connection-refused "Permalink")Database connection refused

1. Verify PostgreSQL is running: `docker compose ps postgres`
2. Check PostgreSQL logs: `docker compose logs postgres --tail 20`
3. Ensure `DB_PASSWORD` matches between the app and postgres containers (both read from the same `.env` variable by default)

 [  Edit this page on GitHub ](https://github.com/Relaticle/relaticle/edit/main/packages/Documentation/resources/content/docs/guides/self-hosting.md) [  Ask a question in Discord ](https://relaticle.com/discord) [  Contact support ](https://relaticle.com/contact)
