Linux
Deploy RAMP on a bare-metal or VM-based Linux server using systemd services and Nginx as a reverse proxy. This guide covers Debian 13, but the steps apply to most systemd-based distributions with minor adjustments.
Architecture overview
Section titled “Architecture overview”The Linux deployment runs three services:
| Service | Runs as | Listens on | Purpose |
|---|---|---|---|
| RAMP API | systemd (ramp.service) | localhost:5000 | Backend API + embedded frontend |
| Keycloak | systemd (keycloak.service) | localhost:8180 | OIDC identity provider (optional) |
| Nginx | system service | ports 80, 443, 8443 | TLS termination and reverse proxy |
RAMP uses SQLite for storage and local file storage for uploads. Keycloak is optional — you can use RAMP’s built-in authentication instead.
Prerequisites
Section titled “Prerequisites”- Debian 13 (or Ubuntu 22.04+, RHEL 9+, other systemd-based distros)
- Root access (or sudo privileges)
- 2 GB RAM minimum (4 GB recommended with Keycloak)
- 5 GB disk space minimum
- Published RAMP build output (built on a development machine)
- SSL certificates (for HTTPS — recommended for production)
Quick start
Section titled “Quick start”The deployment consists of four phases, orchestrated by the deploy-all.sh script:
- VM setup — install .NET Runtime, Java (for Keycloak), and Nginx.
- Keycloak installation — download and configure Keycloak as a systemd service (optional).
- RAMP deployment — deploy the application and register it as a systemd service.
- Nginx configuration — configure the reverse proxy with TLS termination.
# Run everything in one go (as root)sudo ./deploy-all.shOr run each phase individually, as described below.
Phase 1: VM setup
Section titled “Phase 1: VM setup”The setup-vm.sh script installs all prerequisites and creates the required directory structure.
What it installs
Section titled “What it installs”- Essential tools — curl, wget, gnupg, unzip, net-tools, htop
- ASP.NET Core 10 Runtime — from the Microsoft package repository
- Java 21 (OpenJDK) — required for Keycloak
- Nginx — reverse proxy
Run the setup
Section titled “Run the setup”sudo ./setup-vm.shWhat it creates
Section titled “What it creates”| Path | Purpose |
|---|---|
/opt/ramp/app/ | RAMP application files |
/var/ramp/data/ | SQLite database |
/var/ramp/data/files/ | Uploaded file storage |
/var/ramp/logs/ | Application logs |
/opt/keycloak/ | Keycloak installation |
The script also creates a ramp system user with no login shell, which the RAMP service runs under.
Manual installation
Section titled “Manual installation”If you prefer to install prerequisites manually:
# Add Microsoft repositorywget -q https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb \ -O /tmp/packages-microsoft-prod.debsudo dpkg -i /tmp/packages-microsoft-prod.deb
# Install runtimessudo apt-get updatesudo apt-get install -y aspnetcore-runtime-10.0 openjdk-21-jre-headless nginx# Add Microsoft repositorysudo rpm -Uvh https://packages.microsoft.com/config/rhel/9/packages-microsoft-prod.rpm
# Install runtimessudo dnf install -y aspnetcore-runtime-10.0 java-21-openjdk-headless nginxPhase 2: Keycloak installation (optional)
Section titled “Phase 2: Keycloak installation (optional)”The install-keycloak.sh script downloads Keycloak 24.0, sets up realm import, and registers it as a systemd service.
sudo ./install-keycloak.shWhat happens
Section titled “What happens”- Downloads Keycloak 24.0 to
/opt/keycloak/keycloak-24.0.0/ - Creates a symlink at
/opt/keycloak/current - Copies the realm import file (
realm-export.json) for automatic realm provisioning - Installs and starts the
keycloak.service - Waits for the health check (up to 120 seconds)
Keycloak systemd service
Section titled “Keycloak systemd service”[Unit]Description=Keycloak Identity ProviderAfter=network.target
[Service]Type=execUser=rootGroup=root
Environment=KEYCLOAK_ADMIN=adminEnvironment=KEYCLOAK_ADMIN_PASSWORD=Passw0rdEnvironment=KC_HEALTH_ENABLED=trueEnvironment=KC_HTTP_PORT=8180
ExecStart=/opt/keycloak/current/bin/kc.sh start-dev --import-realm --http-port=8180
Restart=on-failureRestartSec=10
[Install]WantedBy=multi-user.targetAccess Keycloak
Section titled “Access Keycloak”- Admin Console:
http://localhost:8180/admin - Credentials:
admin/Passw0rd - RAMP Realm: pre-imported from
realm-export.json
Manage the Keycloak service
Section titled “Manage the Keycloak service”sudo systemctl status keycloaksudo systemctl stop keycloaksudo systemctl start keycloaksudo journalctl -u keycloak -fPhase 3: RAMP deployment
Section titled “Phase 3: RAMP deployment”Build RAMP on your development machine
Section titled “Build RAMP on your development machine”Before deploying, publish the RAMP application:
# Backendcd src/RAMP.APIdotnet publish -c Release -o ./publish
# Frontend (built and embedded in the API publish output)cd src/RAMP.Webnpm ci && npm run buildTransfer files to the server
Section titled “Transfer files to the server”# SCP the published output to the serverscp -r src/RAMP.API/publish/* user@your-server:/opt/ramp/app/Deploy the application
Section titled “Deploy the application”sudo ./deploy-ramp.shWhat happens
Section titled “What happens”- Stops the existing RAMP service (if running)
- Copies
appsettings.Production.jsonandscaffold.yamlto the app directory - Sets file ownership to the
rampsystem user - Installs the systemd service file
- Starts RAMP and waits for the health check (up to 60 seconds)
RAMP systemd service
Section titled “RAMP systemd service”[Unit]Description=RAMP ApplicationAfter=network.targetWants=keycloak.service
[Service]Type=execUser=rampGroup=rampWorkingDirectory=/opt/ramp/app
Environment=ASPNETCORE_ENVIRONMENT=ProductionEnvironment=ASPNETCORE_URLS=http://localhost:5000Environment=DOTNET_ENVIRONMENT=Production
ExecStart=/usr/bin/dotnet /opt/ramp/app/RAMP.API.dll
Restart=on-failureRestartSec=5
LimitNOFILE=65536
[Install]WantedBy=multi-user.targetManage the RAMP service
Section titled “Manage the RAMP service”sudo systemctl status rampsudo systemctl stop rampsudo systemctl start rampsudo systemctl restart rampsudo journalctl -u ramp -fPhase 4: Nginx configuration
Section titled “Phase 4: Nginx configuration”Nginx serves as a TLS-terminating reverse proxy in front of RAMP and Keycloak.
Install the configuration
Section titled “Install the configuration”# Copy the config filesudo cp nginx/ramp-linux.conf /etc/nginx/sites-available/ramp-linux.conf
# Enable the sitesudo ln -sfn /etc/nginx/sites-available/ramp-linux.conf /etc/nginx/sites-enabled/ramp-linux.conf
# Remove the default sitesudo rm -f /etc/nginx/sites-enabled/default
# Test and reloadsudo nginx -t && sudo systemctl reload nginxSSL certificates
Section titled “SSL certificates”Place your SSL certificates at these paths (or adjust the Nginx config):
| File | Path |
|---|---|
| Certificate | /etc/ssl/your-domain/cert.crt |
| Private key | /etc/ssl/your-domain/cert.key |
| CA bundle | /etc/ssl/your-domain/ca-bundle.crt |
Update the ssl_certificate, ssl_certificate_key, and ssl_trusted_certificate directives in the Nginx config to match your paths.
Nginx configuration overview
Section titled “Nginx configuration overview”The Nginx config defines three server blocks:
| Server block | Listens on | Purpose |
|---|---|---|
| HTTP redirect | Port 80 | Redirects all HTTP traffic to HTTPS |
| RAMP (HTTPS) | Port 443 | Proxies to RAMP API on localhost:5000 |
| Keycloak (HTTPS) | Port 8443 | Proxies to Keycloak on localhost:8180 |
Key proxy locations in the RAMP server block:
| Location | Backend | Notes |
|---|---|---|
/_api/ | localhost:5000/_api/ | Backend API endpoints |
/_hubs/ | localhost:5000/_hubs/ | SignalR WebSocket with upgrade headers, 7-day timeout |
/_swagger/ | localhost:5000/_swagger/ | Swagger UI |
/_health | localhost:5000/_health | Health check (access log disabled) |
/ | localhost:5000/ | Frontend SPA (catch-all with fallback to index.html) |
| Static files | localhost:5000 | 1-year cache, Cache-Control: public, immutable |
Application configuration
Section titled “Application configuration”appsettings.Production.json
Section titled “appsettings.Production.json”The production configuration file controls database, authentication, and service settings:
{ "Database": { "Provider": "Sqlite", "ConnectionString": "Data Source=/var/ramp/data/ramp.db", "AutoMigrate": true }, "Jwt": { "Secret": "CHANGE_THIS_IN_PRODUCTION_MIN_32_CHARS", "Issuer": "https://your-domain.com", "Audience": "https://your-domain.com", "AccessTokenExpirationMinutes": 480, "RefreshTokenExpirationDays": 30 }, "CORS": { "AllowedOrigins": ["https://your-domain.com"] }, "Cache": { "Provider": "InMemory" }, "FileStorage": { "Provider": "Local", "LocalFileStorage": { "BasePath": "/var/ramp/data/files" } }, "SignalR": { "UseRedisBackplane": false }, "ForwardedHeaders": { "Enabled": true, "ForwardedHeaders": "All", "KnownProxies": ["127.0.0.1", "::1"] }}For full configuration reference, see Application Settings.
Scaffold file
Section titled “Scaffold file”The scaffold file (default.yaml) seeds the database with tenants, users, IDP connections, and role mappings on first startup.
Key sections of the scaffold:
- Tenant definition with RAMP Internal and Keycloak OIDC identity providers
- Admin user with Administrator and TenantAdministrator roles
- Role mappings from Keycloak realm roles and groups to RAMP roles
Default credentials
Section titled “Default credentials”| Service | URL | Username | Password |
|---|---|---|---|
| RAMP | https://your-domain.com | admin | Passw0rd |
| Keycloak | https://your-domain.com:8443/admin | admin | Passw0rd |
Health checks
Section titled “Health checks”# RAMP healthcurl -s http://localhost:5000/_health
# Keycloak healthcurl -s http://localhost:8180/health/ready
# Nginx statussudo systemctl status nginxTroubleshooting
Section titled “Troubleshooting”RAMP fails to start
Section titled “RAMP fails to start”# Check service statussudo systemctl status ramp
# View detailed logssudo journalctl -u ramp -f
# Check application logsls -la /var/ramp/logs/Common causes:
- Missing
appsettings.Production.json - Database directory permissions (must be owned by
rampuser) - .NET runtime not installed
Keycloak fails to start
Section titled “Keycloak fails to start”# Check service statussudo systemctl status keycloak
# View logssudo journalctl -u keycloak -fCommon causes:
- Java 21 not installed
- Port 8180 already in use
- Insufficient memory (Keycloak needs ~512 MB)
Nginx returns 502 Bad Gateway
Section titled “Nginx returns 502 Bad Gateway”Cause: RAMP is not running or not listening on localhost:5000.
# Check if RAMP is listeningsudo ss -tlnp | grep 5000
# Restart RAMPsudo systemctl restart rampPermission errors
Section titled “Permission errors”# Fix ownershipsudo chown -R ramp:ramp /opt/rampsudo chown -R ramp:ramp /var/rampSSL certificate issues
Section titled “SSL certificate issues”# Test Nginx configurationsudo nginx -t
# Check certificate expiryopenssl x509 -in /etc/ssl/your-domain/cert.crt -noout -datesBackup and restore
Section titled “Backup and restore”Backup
Section titled “Backup”# Databasecp /var/ramp/data/ramp.db /backups/ramp_$(date +%Y%m%d).db
# Uploaded filestar czf /backups/ramp_files_$(date +%Y%m%d).tar.gz /var/ramp/data/files/
# Application configcp /opt/ramp/app/appsettings.Production.json /backups/Restore
Section titled “Restore”# Stop RAMPsudo systemctl stop ramp
# Restore databasecp /backups/ramp_20260301.db /var/ramp/data/ramp.dbchown ramp:ramp /var/ramp/data/ramp.db
# Start RAMPsudo systemctl start rampUpdating RAMP
Section titled “Updating RAMP”-
Build a new release on your development machine.
-
Transfer the published files to the server:
Terminal window scp -r src/RAMP.API/publish/* user@your-server:/opt/ramp/app/ -
Re-run the deployment script:
Terminal window sudo ./deploy-ramp.sh
The deployment script stops the service, copies configuration files, and restarts with the new version.
Production checklist
Section titled “Production checklist”- Change all default passwords (RAMP admin, Keycloak admin, JWT secret)
- Install valid SSL certificates
- Configure CORS to only allow your production domain
- Set up automated backups for the database and file storage
- Configure log rotation for
/var/ramp/logs/ - Set up monitoring for the health endpoints
- Consider upgrading to SQL Server or PostgreSQL for production workloads
- Consider adding Redis for caching and SignalR backplane if scaling