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 |
Compared to traditional stacks (IIS + .NET + SQL Server), mORMot requires:
- 10x less RAM
- Simpler configuration
- No additional dependencies
The simplest deployment — a single executable:
┌─────────────────────────────────────┐
│ Application │
│ ┌─────────┐ ┌─────────────────┐ │
│ │ Client │──│ Server (HTTP) │ │
│ │ Code │ │ + ORM + SOA │ │
│ └─────────┘ └─────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ SQLite3 │ │
│ │ Database │ │
│ └─────────────┘ │
└─────────────────────────────────────┘
Perfect for:
- Desktop applications with local data
- Development and testing
- Single-user scenarios
// 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:
- Small to medium applications
- Corporate intranets
- Single-location deployments
For security-sensitive deployments:
Internet DMZ Internal Network
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌───────┴───────┐
│ AJAX/Web │ │ Services │ │ ORM │
│ Clients │─────────> │ Server │──────────> │ Server │
└─────────────┘ │ (Stateless)│ │ + Database │
└─────────────┘ └───────────────┘
│
┌─────┴─────┐
│ Firewall │
└───────────┘
Benefits:
- Database never exposed to Internet
- Services can be stateless (scalable)
- Clear security boundaries
Implementation:
// 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 │ │ Client │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└──────┬──────┴──────┬──────┘
▼ │
┌──────────────┐ │
│ Gateway │ │
│ Server │ │
└──────┬───────┘ │
│ │
┌──────┼─────────────┤
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Auth │ │ Orders │ │ Reports│
│ Service│ │ Service│ │ Service│
└────────┘ └────────┘ └────────┘
Each service:
- Has its own database
- Independently deployable
- Communicates via REST/JSON
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(TSynDaemon)
protected
fServer: TRestServerDB;
fHttp: TRestHttpServer;
public
procedure Start; override;
procedure Stop; override;
end;
procedure TMyDaemon.Start;
begin
fServer := TRestServerDB.Create(Model, 'data.db3');
fHttp := TRestHttpServer.Create('8080', [fServer]);
end;
procedure TMyDaemon.Stop;
begin
fHttp.Free;
fServer.Free;
end;
begin
with TMyDaemon.Create(TSynDaemonSettings, '', '', '') do
try
CommandLine; // Handles install/start/stop/console on Windows and Linux
finally
Free;
end;
end.Service management:
:: Install service
MyServerService.exe /install
:: Start service
net start MyServer
:: Stop service
net stop MyServer
:: Uninstall service
MyServerService.exe /uninstallFor best Windows performance, use http.sys:
HttpServer := TRestHttpServer.Create('8080', [Server], '+',
useHttpApiRegisteringURI); // Uses http.sysURL reservation (run as Administrator):
netsh http add urlacl url=http://+:8080/ user=EveryoneSSL 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.targetManagement:
# 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 -fDockerfile:
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=debugSystem 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
TSynAngelize is mORMot's cross-platform process manager — similar to NSSM (Windows) or supervisord (Linux), but JSON-configured and integrated with the daemon framework.
Key Features:
- Launch and monitor multiple sub-processes
- Auto-restart on crash with configurable backoff
- Health checks via HTTP endpoints
- Notifications on failure (email, HTTP, log, exec)
- Cross-platform (Windows services + POSIX daemons)
- Console output redirection and rotation
┌──────────────────────────────────────────────────┐
│ TSynAngelize │
│ (Main Launcher/Watcher) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Service 1 │ │ Service 2 │ │ Service N│ │
│ │ (API Server)│ │ (Worker) │ │ (...) │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
│ │ │ │ │
│ [Watch] [Watch] [Watch] │
│ Health Process Custom │
│ Check Monitor Script │
└──────────────────────────────────────────────────┘
Configuration stored in <exename>.settings JSON file:
{
"Services": [
{
"Name": "api",
"Description": "REST API Server",
"Run": "/opt/app/api-server",
"Start": [ "start:%run%" ],
"Stop": [ "stop:%run%" ],
"Watch": [ "http://127.0.0.1:8080/health=200" ],
"WatchDelaySec": 60,
"RetryStableSec": 120,
"RedirectLogFile": "%log%api-console.log",
"RedirectLogRotateFiles": 5,
"RedirectLogRotateBytes": 10485760,
"Notify": "admin@example.com,%log%alerts.log"
},
{
"Name": "worker",
"Description": "Background Worker",
"Run": "/opt/app/worker",
"Start": [ "start:%run%" ],
"Stop": [ "stop:%run%" ],
"AbortExitCodes": [ 99 ]
}
]
}Actions in Start, Stop, and Watch arrays:
| Action | Description |
|---|---|
start:/path/to/exe |
Launch and monitor process (like NSSM) |
stop:/path/to/exe |
Stop monitored process |
exec:/path/to/exe |
Execute without waiting |
wait:/path/to/exe |
Execute and wait for exit code 0 |
http://host/path |
HTTP GET request (expects 200) |
http://host/path=CODE |
HTTP GET expecting specific status code |
sleep:1000 |
Wait N milliseconds |
service:Name |
Control Windows service (Windows only) |
Variables:
%run%— Expands to theRunproperty value%log%— Expands to the log directory path
When a monitored process exits unexpectedly:
- Immediate restart attempted
- If crash recurs within
RetryStableSec, backoff delay increases - Process considered "stable" after running
RetryStableSecseconds AbortExitCodescan prevent restart (e.g., exit code 99 = don't restart)Notifytriggered on persistent failures
The Watch array defines periodic health checks:
{
"Watch": [
"http://127.0.0.1:8080/health=200",
"http://127.0.0.1:8080/ready"
],
"WatchDelaySec": 30
}If health check fails:
- Process is considered unhealthy
- Restart triggered
- Notification sent if
Notifyconfigured
program ProcessManager;
uses
mormot.app.agl;
type
TMyAngelize = class(TSynAngelize)
end;
begin
with TMyAngelize.Create do
try
CommandLine; // Handles install/start/stop/console
finally
Free;
end;
end.Command line:
# Windows
ProcessManager.exe /install
ProcessManager.exe /start
ProcessManager.exe /console # Run in foreground for debugging
# Linux
./ProcessManager --run # Run as daemon
./ProcessManager --console # Run in foreground
./ProcessManager --kill # Stop daemon| Feature | Angelize | NSSM | supervisord | systemd |
|---|---|---|---|---|
| Cross-platform | ✅ | ❌ Windows | ❌ Linux | ❌ Linux |
| JSON config | ✅ | ❌ | ❌ INI | ❌ Unit files |
| Health checks | ✅ HTTP | ❌ | ✅ | ✅ |
| Log rotation | ✅ | ❌ | ✅ | ✅ journald |
| Notifications | ✅ Email/HTTP/Log | ❌ | ❌ | ❌ |
| mORMot integration | ✅ Native | ❌ | ❌ | ❌ |
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.comOr 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)
Important: Authenticated endpoints must bypass cache:
// 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 a custom TAuthSession descendant with external session storage for distributed state.
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).
- Use HTTPS in production
- Configure firewall (only expose needed ports)
- Use reverse proxy for SSL termination
- Enable rate limiting
- Configure CORS properly
- Enable authentication for sensitive endpoints
- Use strong password hashing (SHA-256 + salt)
- Implement proper authorization (per-method)
- Validate all input
- Sanitize output (automatic with Mustache)
- Run as non-root user
- Minimize installed packages
- Keep system updated
- Configure log rotation
- Set up monitoring/alerting
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 |
Key takeaways:
- mORMot requires minimal resources
- Single executable deployment
- Native cross-platform support
- Easy integration with standard infrastructure
- Built-in logging and monitoring support
| Previous | Index | Next |
|---|---|---|
| Chapter 19: MVC/MVVM Web Applications | Index | Chapter 21: Security |