From Development to Production
This chapter covers deployment patterns for mORMot 2 applications, from simple stand-alone executables to complex multi-server architectures with CDN integration.
mORMot 2 natively supports:
| Platform | Compiler | Notes |
|---|---|---|
| Windows (32/64-bit) | Delphi, FPC | Full support including http.sys |
| Linux (x86_64, aarch64) | Delphi 12+, FPC | Recommended for servers |
| macOS (x86_64, aarch64) | Delphi, FPC | Development and servers |
| FreeBSD | FPC | Server deployments |
| Android | Delphi, FPC | Client applications |
mORMot applications are extremely efficient:
| Component | Typical Requirement |
|---|---|
| RAM | 50-200 MB for typical server |
| CPU | Minimal; single-core sufficient for most workloads |
| Storage | SQLite3 database + application (~5-50 MB) |
| Network | Standard HTTP/HTTPS ports |
The simplest deployment — a single executable:
┌─────────────────────────────────────┐
│ Application │
│ ┌─────────┐ ┌─────────────────┐ │
│ │ Client │──│ Server (HTTP) │ │
│ │ Code │ │ + ORM + SOA │ │
│ └─────────┘ └─────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ SQLite3 │ │
│ │ Database │ │
│ └─────────────┘ │
└─────────────────────────────────────┘
Perfect for:
// In-process server access
var
Server: TRestServerDB;
Client: TRestClientDB;
begin
Server := TRestServerDB.Create(Model, 'data.db3');
Client := TRestClientDB.Create(Server);
// Client and server in same process
end;
One server handling both ORM and SOA:
┌──────────┐ ┌──────────┐
│ Client 1 │───┐ │ Client 2 │
│ (Delphi) │ │ │ (AJAX) │
└──────────┘ │ └──────────┘
│ │
▼ ▼
┌─────────────────────┐
│ HTTP Server │
│ ┌───────────────┐ │
│ │ ORM + SOA │ │
│ └───────┬───────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ SQLite3 │ │
│ └───────────┘ │
└─────────────────────┘
Suitable for:
For security-sensitive deployments:
Internet DMZ Internal Network
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌───────┴───────┐
│ AJAX/Web │ │ Services │ │ ORM │
│ Clients │─────────▶ │ Server │──────────▶ │ Server │
└─────────────┘ │ (Stateless)│ │ + Database │
└─────────────┘ └───────────────┘
│
┌─────┴─────┐
│ Firewall │
└───────────┘
Benefits:
// Services server (in DMZ)
Server := TRestServerFullMemory.Create(Model);
Server.ServiceDefine(TMyService, [IMyService], sicShared);
// Connect to internal ORM server
Server.RemoteDataCreate(InternalOrmClient, TOrmArticle);
// Internal ORM server
OrmServer := TRestServerDB.Create(Model, 'data.db3');
Multiple specialized servers:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │ │ Client │ │ C│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└──────┬──────┴──────┬──────┘
▼ │
┌──────────────┐ │
│ Gateway │ │
│ Server │ │
└──────┬───────┘ │
│ │
┌──────┼──────┬──────┼──────┐
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Auth │ │ Orders │ │ Reports│
│ Service│ │ Service│ │ Service│
└────────┘ └────────┘ └────────┘
Each service:
Simplest deployment for development:
program MyServer;
{$APPTYPE CONSOLE}
begin
// Server initialization
WriteLn('Server running on http://localhost:8080');
ReadLn; // Wait for Enter to stop
end.
For production deployment:
program MyServerService;
uses
mormot.app.daemon;
type
TMyDaemon = class(TDaemon)
protected
fServer: TRestServerDB;
fHttp: TRestHttpServer;
procedure DoStart; override;
procedure DoStop; override;
end;
procedure TMyDaemon.DoStart;
begin
fServer := TRestServerDB.Create(Model, 'data.db3');
fHttp := TRestHttpServer.Create('8080', [fServer]);
end;
procedure TMyDaemon.DoStop;
begin
fHttp.Free;
fServer.Free;
end;
begin
TDaemonService.Create(TMyDaemon, 'MyServer', 'My mORMot Server');
TDaemonService.RunAsService;
end.
Service management:
:: Install service
MyServerService.exe /install
:: Start service
net start MyServer
:: Stop service
net stop MyServer
:: Uninstall service
MyServerService.exe /uninstall
For best Windows performance, use http.sys:
HttpServer := TRestHttpServer.Create('8080', [Server], '+',
useHttpApiRegisteringUri); // Uses http.sys
URL reservation (run as Administrator):
netsh http add urlacl url=http://+:8080/ user=Everyone
SSL certificate binding:
netsh http add sslcert ipport=0.0.0.0:443 ^
certhash=YOUR_CERT_THUMBPRINT ^
appid={YOUR_APP_GUID}
Create /etc/systemd/system/mormot-server.service:
[Unit]
Description=mORMot Server
After=network.target
[Service]
Type=simple
User=mormot
Group=mormot
WorkingDirectory=/opt/mormot
ExecStart=/opt/mormot/myserver
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Management:
# Enable and start
sudo systemctl enable mormot-server
sudo systemctl start mormot-server
# Check status
sudo systemctl status mormot-server
# View logs
sudo journalctl -u mormot-server -f
Dockerfile:
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
libsqlite3-0 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY myserver /app/
COPY Views/ /app/Views/
EXPOSE 8080
USER nobody:nogroup
CMD ["/app/myserver"]
docker-compose.yml:
version: '3.8'
services:
mormot-server:
build: .
ports:
- "8080:8080"
volumes:
- ./data:/app/data
restart: unless-stopped
environment:
- MORMOT_LOG_LEVEL=debug
System limits (/etc/security/limits.conf):
mormot soft nofile 65535
mormot hard nofile 65535
Kernel parameters (/etc/sysctl.conf):
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
upstream mormot {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://mormot;
proxy_http_version 1.1;
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 Connection "";
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static files served by Nginx
location /.static/ {
alias /opt/mormot/static/;
expires 30d;
}
}
<VirtualHost *:80>
ServerName api.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:8080/$1 [P,L]
</VirtualHost>
With Let's Encrypt (certbot):
sudo certbot --nginx -d api.example.com
Or manually in Nginx:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://127.0.0.1:8080;
# ... proxy settings
}
}
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │ │ Client │ │ Client │
│ (US) │ │ (EU) │ │ (Asia) │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ CDN │ │ CDN │ │ CDN │
│ Edge US │ │ Edge EU │ │Edge Asia│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└───────────────┼───────────────┘
│
▼
┌─────────────┐
│ Origin │
│ Server │
│ (mORMot) │
└─────────────┘
Enable caching for appropriate endpoints:
procedure TMyServer.GetPublicData(Ctxt: TRestServerUriContext);
begin
Ctxt.Returns(Data, HTTP_SUCCESS,
'Content-Type: application/json'#13#10 +
'Cache-Control: public, max-age=300', // 5 minutes
True); // Handle304NotModified
end;
Page Rules:
api.example.com/public/ → Cache Everything, Edge TTL: 5 minutesapi.example.com/auth/ → Bypass Cacheapi.example.com/api/* → Bypass Cache (authenticated)// Disable authentication for cacheable endpoints
Server.ServiceMethodByPassAuthentication('GetPublicData');
// Configure logging
with TSynLog.Family do
begin
Level := LOG_VERBOSE;
DestinationPath := '/var/log/mormot/';
RotateFileCount := 10;
RotateFileSizeKB := 10240; // 10 MB per file
end;
procedure TMyServer.Health(Ctxt: TRestServerUriContext);
var
Status: TDocVariantData;
begin
Status.InitObject([
'status', 'ok',
'timestamp', NowUtc,
'uptime', GetTickCount64 - fStartTime,
'connections', fActiveConnections
]);
Ctxt.Returns(Status.ToJson);
end;
// Register without authentication
Server.ServiceMethodByPassAuthentication('Health');
procedure TMyServer.Metrics(Ctxt: TRestServerUriContext);
var
Info: TDocVariantData;
begin
Info.InitObject([
'requests_total', fRequestCount,
'requests_per_second', fRequestsPerSecond,
'memory_mb', GetHeapStatus.TotalAllocated div (1024*1024),
'db_connections', Server.DB.ConnectionCount,
'active_sessions', Server.Sessions.Count
]);
Ctxt.Returns(Info.ToJson);
end;
Multiple mORMot instances behind a load balancer:
upstream mormot_cluster {
least_conn;
server 10.0.0.1:8080 weight=5;
server 10.0.0.2:8080 weight=5;
server 10.0.0.3:8080 backup;
keepalive 32;
}
For stateful services, use sticky sessions:
upstream mormot_cluster {
ip_hash; # Same client always goes to same server
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
Or use external session storage (Redis):
// Store sessions externally
Server.SessionClass := TAuthSessionRedis;
For high availability with SQLite3:
// Master server
MasterServer := TRestServerDB.Create(Model, 'master.db3');
// Replica servers (read-only)
ReplicaServer := TRestServerDB.Create(Model, 'replica.db3');
ReplicaServer.DB.OpenV2('replica.db3', SQLITE_OPEN_READONLY);
Or use external databases with built-in replication (PostgreSQL, MySQL).
mORMot 2 deployment options:
| Scenario | Recommended Setup |
|---|---|
| Development | Console application |
| Windows Production | Windows Service + http.sys |
| Linux Production | systemd + Nginx |
| Containers | Docker with Alpine/Debian |
| High Traffic | Load balancer + CDN |
| High Availability | Cluster + session sharing |
| Previous | Index | Next |
|---|---|---|
| Chapter 19: MVC/MVVM Web Applications | Index | Chapter 21: Security |