Kc
Applications Azure

Krayin CRM on Ubuntu 24.04 on Azure User Guide

| Product: Krayin CRM on Ubuntu 24.04 LTS on Azure

Overview

Krayin CRM is an open source customer relationship management application built on Laravel. One web interface covers leads moving through a configurable pipeline, persons and organizations, quotes built from a product catalogue, an activity timeline of emails, calls, meetings and notes, web forms that capture enquiries straight into the pipeline, workflow automation, and role based permissions. The cloudimg image delivers the official Krayin CRM 2.2.6 release fully installed on Ubuntu 24.04, served over HTTPS, with every per VM secret generated on first boot, so a working CRM is serving within minutes of launch. Backed by 24/7 cloudimg support.

Krayin CRM is free software published under the MIT licence. Krayin is a trademark of its respective owner; cloudimg is not affiliated with, endorsed by, or sponsored by Webkul or the Krayin project. This image packages the upstream open source release, without any of the commercial add ons sold separately upstream, together with cloudimg's provisioning, hardening and support.

What is included:

  • Krayin CRM 2.2.6, served from /var/www/krayin, with the document root set to its public/ directory
  • PHP 8.3 (php8.3-fpm, over a local UNIX socket) behind Apache 2.4, reachable at https://<vm-ip>/; port 80 only ever redirects to HTTPS and serves no content of its own
  • A local MySQL 8 database, bound to 127.0.0.1 only
  • A per VM administrator password, Laravel application key, database password and HTTPS certificate, all generated on first boot and written to a root only file. No default, shared or published credential ships in the image: Krayin's upstream seeder hardcodes a published administrator address and password, and in this image neither half survives the build
  • The database is installed and migrated at build time from the command line. The Krayin web installer (/install and its API endpoints) is blocked, so there is no setup route for a stranger to reach — even if the application were made to believe it was not installed
  • Generated links and password reset emails always point at this VM's own address
  • 24/7 cloudimg support

The Krayin CRM sign in page served over HTTPS

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended starting size for a small sales team; scale up for many concurrent users or large CSV/XLSX imports. NSG inbound: allow 22/tcp from your management network and 443/tcp (HTTPS) from wherever your team will browse. 80/tcp is optional and only redirects to HTTPS.

Step 1: Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Krayin CRM by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size (Standard_B2s or larger); under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTPS (443). Then Review + create and Create.

Step 2: Deploy from the Azure CLI

cloudimg images carry a Marketplace purchase plan, so accept its terms once per subscription, then create the VM and open HTTPS:

az vm image terms accept --publisher cloudimg --offer krayin-ubuntu-24-04 --plan default

az vm create \
  --resource-group my-rg \
  --name my-krayin \
  --image cloudimg:krayin-ubuntu-24-04:default:latest \
  --plan-name default --plan-product krayin-ubuntu-24-04 --plan-publisher cloudimg \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group my-rg --name my-krayin --port 443 --priority 1001

Step 3: Connect to your VM

ssh azureuser@<vm-public-ip>

First boot takes two to three minutes. It generates this VM's administrator password, Laravel application key, database password and HTTPS certificate, and only then starts the web server — so if the site does not answer immediately, first boot has not finished yet. That ordering is deliberate: Apache and PHP-FPM are held closed until first boot has succeeded, so a failed first boot leaves the VM unreachable rather than serving a half configured CRM.

Step 4: Confirm the Krayin stack is running

sudo systemctl is-active mysql php8.3-fpm apache2 krayin-firstboot

All four report active. Now check what is listening and that the application answers:

sudo ss -lnt | grep -E ':(22|80|443|3306) ' | sort

MySQL appears only on 127.0.0.1:3306 — it is never a network service on this appliance. Ports 22, 80 and 443 are the only ones reachable from outside.

curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/admin/login

200 confirms the sign in page is being served through Apache and PHP-FPM. Check the installed release:

grep "const KRAYIN_VERSION" /var/www/krayin/packages/Webkul/Core/src/Core.php

Services active, MySQL on loopback only, and the Krayin version

Step 5: Retrieve the first boot credentials

Every secret on this VM was generated on its own first boot and written to a file only root can read:

sudo cat /root/krayin-credentials.txt

The file looks like this, with the real values unique to your VM:

# cloudimg :: Krayin CRM appliance credentials
# Generated on this VM's first boot. This file is readable only by root.
# Sign in at https://<this-vm-address>/admin/login and change the password afterwards.
krayin.admin.user=admin@cloudimg.local
krayin.admin.pass=<unique to this VM>
krayin.db.name=krayin
krayin.db.user=krayin
krayin.db.pass=<unique to this VM>
KRAYIN_APP_URL=https://<this-vm-address>

Confirm the file is readable only by root, and that the Laravel application key is this VM's own:

sudo stat -c '%a %U:%G %n' /root/krayin-credentials.txt /var/www/krayin/.env
sudo grep -c '^APP_KEY=base64:' /var/www/krayin/.env

The root only credentials file, with the per VM passwords hidden

Step 6: Sign in

Browse to https://<vm-public-ip>/admin/login. Your browser will warn about the self signed certificate until you install your own (Step 11) — accept the warning to continue. Sign in with the address and password from the credentials file.

The Krayin CRM dashboard after signing in

Change the administrator password once you are in, under Settings, then Users. The address admin@cloudimg.local is a local placeholder; edit the same user to set your own real email address so password reset works.

Step 7: Create your first lead

Krayin organises sales work as leads moving through pipeline stages. Choose Leads in the sidebar, then Create Lead. Give the lead a title and an estimated value, pick a Source and Type, and under Name choose Click to Add to attach a contact person — typing a name that does not exist yet offers Add as New. If the lead has no line items, remove the empty row on the Products tab before saving, since a blank product row fails validation.

A saved lead sitting in the Default Pipeline

Saved leads appear in the pipeline immediately, and the dashboard totals update. Open the lead to see its full record and an automatic changelog of every field that changed.

The saved lead re-opened, with its changelog

You can confirm from the shell that what you entered really persisted to the database:

sudo mysql --defaults-file=/etc/mysql/debian.cnf -D krayin -e "SELECT id, title, lead_value, created_at FROM leads ORDER BY id DESC LIMIT 5;"

Step 8: There is no default login, and the installer is blocked

Krayin's upstream seeder creates an administrator whose address and password are published in the project's own source. This image replaces both halves before the application is ever reachable, so that account simply does not exist here:

sudo mysql --defaults-file=/etc/mysql/debian.cnf -D krayin -e "SELECT id, name, email FROM users;"

Only your own administrator is listed. The web installer is refused outright, and so is the environment file:

for p in /admin/login /install /install/api/run-seeder /.env; do printf '%-26s %s\n' "$p" "$(curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1$p)"; done

/admin/login answers 200; every installer endpoint and /.env answers 403.

A note on status codes. Krayin renders its own "404 Page Not Found" view with HTTP 200 rather than 404. So for a path that is simply not routed, a 200 does not mean the file was served — check the body, not the status. If you put this VM behind uptime monitoring, alert on page content rather than on the status code alone.

Only your own administrator exists; the installer and .env are refused

Step 9: How Krayin learns its address

Laravel builds absolute URLs — including password reset links and the storage disk URL — from APP_URL. On this image krayin-baseurl.service rewrites APP_URL and Apache's ServerName from the VM's own address on every boot, reissues the certificate for it, and rebuilds Laravel's configuration cache. An Azure public IP that changes across a stop and start is therefore picked up automatically:

grep '^ServerName' /etc/apache2/conf-available/krayin-servername.conf
sudo grep '^APP_URL' /var/www/krayin/.env
openssl x509 -in /etc/ssl/krayin/krayin.crt -noout -subject -ext subjectAltName
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://127.0.0.1/

Port 80 answers 301 to the HTTPS URL and serves nothing else.

ServerName, APP_URL, the certificate and the port 80 redirect

Step 10: Configure outbound email

The image ships the log mail transport, so Krayin writes messages it would have sent to storage/logs/laravel.log rather than failing against a mail server that does not exist. Point it at your own SMTP relay by editing /var/www/krayin/.env:

MAIL_MAILER=smtp
MAIL_HOST=smtp.your-provider.example
MAIL_PORT=587
MAIL_USERNAME=your-smtp-username
MAIL_PASSWORD=your-smtp-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=crm@your-domain.example
MAIL_FROM_NAME="Your Company CRM"

Laravel caches its configuration, so rebuild the cache after any .env change:

cd /var/www/krayin && sudo -u www-data php artisan config:cache

Step 11: Use a domain name and your own certificate

Point a DNS A record at the VM's public IP, then pin the name so it survives reboots and IP changes:

echo "crm.your-domain.example" | sudo tee /etc/krayin/server-name
sudo /usr/local/sbin/krayin-baseurl.sh

That writes ServerName, rewrites APP_URL to the new name and reissues a self signed certificate for it. To replace the self signed certificate with a trusted one:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-apache
sudo certbot --apache -d crm.your-domain.example

Certbot writes its own certificate paths into the Apache configuration. Keep /etc/krayin/server-name in place so APP_URL continues to track the same name.

Step 12: Inbound email and the scheduler

Krayin can turn incoming mail into leads and activities. The image ships a systemd timer, krayin-scheduler.timer, that runs Laravel's scheduler every minute — which is what drives Krayin's inbound-emails:process task. Confirm it is armed:

systemctl list-timers krayin-scheduler.timer --no-pager | head -3

To enable inbound capture, add your IMAP settings to /var/www/krayin/.env and rebuild the config cache as in Step 10:

IMAP_HOST=imap.your-provider.example
IMAP_PORT=993
IMAP_ENCRYPTION=ssl
IMAP_VALIDATE_CERT=true
IMAP_USERNAME=crm@your-domain.example
IMAP_PASSWORD=your-imap-password

The queue runs in sync mode, which is upstream's default: bulk CSV and XLSX imports therefore run inline in the web request. That is why the command line PHP configuration leaves memory_limit uncapped while the web tier is capped at 512M. For very large imports, raise memory_limit in /etc/php/8.3/fpm/conf.d/99-krayin.ini and reload PHP-FPM.

Step 13: Security model

  • No default credential. Krayin's published administrator address and password never survive the build, and your administrator password, Laravel application key and database password are generated on this VM's first boot.
  • The web installer is unreachable. Apache denies /install and every /install/api/ endpoint. This holds even if the application is made to believe it is not installed, which is exactly the state an attacker would try to induce.
  • The environment file is not exposed. The document root is Krayin's public/ directory, so .env sits above it, and dotfiles are denied by name as a second line of defence.
  • Only the front controller executes. Every .php under the document root except public/index.php is denied, so a stray or uploaded script cannot run.
  • The code tree is read only to the web server. Only storage/ and bootstrap/cache are writable by www-data.
  • Debug mode is off and APP_ENV is production, so an error page never leaks a stack trace or configuration.
  • Nothing but SSH and HTTPS is reachable. MySQL and PHP-FPM are on localhost only, and MySQL's X Protocol plugin is disabled.
  • Sign in is rate limited by Krayin itself: five failed attempts per account per minute and twenty per address return HTTP 429.

Review what is listening and the effective SSH policy at any time:

sudo ss -lntu | awk '{print $1, $5}' | sort -u
sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|clientaliveinterval) '

Step 14: Back up your CRM data

A complete backup is the database plus storage/ (uploaded files) and .env (which holds the application key — without it, encrypted values cannot be read):

sudo mysqldump --defaults-file=/etc/mysql/debian.cnf --single-transaction --routines krayin \
  | gzip > /var/backups/krayin-$(date +%F).sql.gz
sudo tar czf /var/backups/krayin-files-$(date +%F).tar.gz \
  -C /var/www/krayin storage .env

You can confirm the dump path works without writing a file by exporting the schema only:

sudo mysqldump --defaults-file=/etc/mysql/debian.cnf --no-data krayin > /dev/null && echo "dump OK"

Store both archives off the VM. To restore, recreate the database, load the dump, restore storage/ and .env, then rebuild the config cache.

Step 15: Maintenance and upgrades

Ubuntu security updates are applied automatically by unattended-upgrades; confirm it is enabled:

grep 'Unattended-Upgrade' /etc/apt/apt.conf.d/20auto-upgrades

Krayin itself is upgraded from upstream's release archive. Read the Krayin upgrade notes for your target version first, take a backup as in Step 14, then replace the application tree, run composer install --no-dev, run php artisan migrate --force, and rebuild the caches. Note that composer install requires the php8.3-zip extension, which this image ships.

Krayin's own scheduled task and the queue are described in Step 12. If you enable a database backed queue instead of sync, remember to run a queue worker.

Support

cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk and live chat. We cover deployment, retrieving the first boot credentials, signing in, users, roles and permissions, pipelines and lead sources, outbound and inbound email, custom domains and HTTPS certificates, backups, upgrades and performance tuning. Questions about Krayin feature behaviour are answered on a best effort basis and, where appropriate, directed to the Krayin documentation at devdocs.krayincrm.com.