Skip to main content

#Self-Hosting Guide

Deploy Relaticle on your own infrastructure with Docker or manually.


Get Relaticle running in 5 steps:

  1. Download the compose file:
curl -o compose.yml https://raw.githubusercontent.com/Relaticle/relaticle/main/compose.yml
  1. Generate an application key:
echo "APP_KEY=base64:$(openssl rand -base64 32)"
  1. 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
  1. Start the containers:
docker compose up -d
  1. Create your admin account:
docker compose exec app php artisan make:filament-user

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


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

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.
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).
LOG_CHANNEL stderr Where logs go. stderr is recommended for Docker.
LOG_LEVEL warning Minimum log level. Use debug for troubleshooting.
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 [email protected] Sender email address.
MAIL_FROM_NAME Relaticle Sender display name.

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.
Variable Description
GOOGLE_CLIENT_ID Google OAuth client ID for social login.
GOOGLE_CLIENT_SECRET Google OAuth client secret.
GITHUB_CLIENT_ID GitHub OAuth client ID for social login.
GITHUB_CLIENT_SECRET GitHub OAuth client secret.
SENTRY_LARAVEL_DSN Sentry DSN for error tracking.
FATHOM_ANALYTICS_SITE_ID Fathom Analytics site ID.

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.
Volume Purpose
postgres Database files. Back this up.
redis Redis persistence data.
storage Uploaded files and application storage. Back this up.

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.


After starting the containers, create your first admin user:

docker compose exec app php artisan make:filament-user

You will be prompted for a name, email, and password. Once created, access the CRM panel at {APP_URL}/app.

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.


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.

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;
}
crm.example.com {
    reverse_proxy 127.0.0.1:80
}

Caddy handles SSL certificates automatically via Let's Encrypt.

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 TRUSTED_PROXIES environment variable defaults to * in Docker, so forwarded headers are handled automatically.


Dokploy is an open-source deployment platform. Here's how to deploy Relaticle on it.

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

  1. Inside your project, click Create Service and select Compose
  2. Set the source to Raw and paste the contents of the compose.yml
  3. Click Create

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.

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

  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)

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.


Coolify is an open-source, self-hostable platform for deploying applications.

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

  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

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
  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

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

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.


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

docker compose pull
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 before upgrading. Breaking changes will be documented there.


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

Back up the storage volume which contains uploaded files:

docker compose cp app:/var/www/html/storage/app ./storage-backup

Add a cron job to back up daily:

0 3 * * * cd /path/to/relaticle && docker compose exec -T postgres pg_dump -U relaticle relaticle | gzip > /backups/relaticle-$(date +\%Y\%m\%d).sql.gz

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

  • PHP 8.4+ with extensions: pdo_pgsql, gd, bcmath, mbstring, xml, redis
  • PostgreSQL 17+
  • Redis 7+
  • Node.js 20+
  • Composer 2+
  • Nginx or Apache
  • Supervisor (for queue workers)
  1. Clone the repository:
git clone https://github.com/Relaticle/relaticle.git /var/www/relaticle
cd /var/www/relaticle
  1. Install dependencies:
composer install --no-dev --optimize-autoloader
npm ci && npm run build
  1. Configure the environment:
cp .env.example .env
php artisan key:generate

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

  1. Set up the database:
php artisan migrate --force
php artisan storage:link
  1. Set permissions:
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
  1. Create your admin user:
php artisan make:filament-user
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.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

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

Add to www-data's crontab:

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

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

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)
  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
  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('[email protected]')->subject('Test'))"
  4. Check logs: docker compose logs app | grep -i mail
  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
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
  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)