Skip to content

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.

The Linux deployment runs three services:

ServiceRuns asListens onPurpose
RAMP APIsystemd (ramp.service)localhost:5000Backend API + embedded frontend
Keycloaksystemd (keycloak.service)localhost:8180OIDC identity provider (optional)
Nginxsystem serviceports 80, 443, 8443TLS 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.


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

The deployment consists of four phases, orchestrated by the deploy-all.sh script:

  1. VM setup — install .NET Runtime, Java (for Keycloak), and Nginx.
  2. Keycloak installation — download and configure Keycloak as a systemd service (optional).
  3. RAMP deployment — deploy the application and register it as a systemd service.
  4. Nginx configuration — configure the reverse proxy with TLS termination.
Terminal window
# Run everything in one go (as root)
sudo ./deploy-all.sh

Or run each phase individually, as described below.


The setup-vm.sh script installs all prerequisites and creates the required directory structure.

  • 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
Terminal window
sudo ./setup-vm.sh
PathPurpose
/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.

If you prefer to install prerequisites manually:

Terminal window
# Add Microsoft repository
wget -q https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb \
-O /tmp/packages-microsoft-prod.deb
sudo dpkg -i /tmp/packages-microsoft-prod.deb
# Install runtimes
sudo apt-get update
sudo apt-get install -y aspnetcore-runtime-10.0 openjdk-21-jre-headless nginx

The install-keycloak.sh script downloads Keycloak 24.0, sets up realm import, and registers it as a systemd service.

Terminal window
sudo ./install-keycloak.sh
  1. Downloads Keycloak 24.0 to /opt/keycloak/keycloak-24.0.0/
  2. Creates a symlink at /opt/keycloak/current
  3. Copies the realm import file (realm-export.json) for automatic realm provisioning
  4. Installs and starts the keycloak.service
  5. Waits for the health check (up to 120 seconds)
[Unit]
Description=Keycloak Identity Provider
After=network.target
[Service]
Type=exec
User=root
Group=root
Environment=KEYCLOAK_ADMIN=admin
Environment=KEYCLOAK_ADMIN_PASSWORD=Passw0rd
Environment=KC_HEALTH_ENABLED=true
Environment=KC_HTTP_PORT=8180
ExecStart=/opt/keycloak/current/bin/kc.sh start-dev --import-realm --http-port=8180
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
  • Admin Console: http://localhost:8180/admin
  • Credentials: admin / Passw0rd
  • RAMP Realm: pre-imported from realm-export.json
Terminal window
sudo systemctl status keycloak
sudo systemctl stop keycloak
sudo systemctl start keycloak
sudo journalctl -u keycloak -f

Before deploying, publish the RAMP application:

Terminal window
# Backend
cd src/RAMP.API
dotnet publish -c Release -o ./publish
# Frontend (built and embedded in the API publish output)
cd src/RAMP.Web
npm ci && npm run build
Terminal window
# SCP the published output to the server
scp -r src/RAMP.API/publish/* user@your-server:/opt/ramp/app/
Terminal window
sudo ./deploy-ramp.sh
  1. Stops the existing RAMP service (if running)
  2. Copies appsettings.Production.json and scaffold.yaml to the app directory
  3. Sets file ownership to the ramp system user
  4. Installs the systemd service file
  5. Starts RAMP and waits for the health check (up to 60 seconds)
[Unit]
Description=RAMP Application
After=network.target
Wants=keycloak.service
[Service]
Type=exec
User=ramp
Group=ramp
WorkingDirectory=/opt/ramp/app
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://localhost:5000
Environment=DOTNET_ENVIRONMENT=Production
ExecStart=/usr/bin/dotnet /opt/ramp/app/RAMP.API.dll
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Terminal window
sudo systemctl status ramp
sudo systemctl stop ramp
sudo systemctl start ramp
sudo systemctl restart ramp
sudo journalctl -u ramp -f

Nginx serves as a TLS-terminating reverse proxy in front of RAMP and Keycloak.

Terminal window
# Copy the config file
sudo cp nginx/ramp-linux.conf /etc/nginx/sites-available/ramp-linux.conf
# Enable the site
sudo ln -sfn /etc/nginx/sites-available/ramp-linux.conf /etc/nginx/sites-enabled/ramp-linux.conf
# Remove the default site
sudo rm -f /etc/nginx/sites-enabled/default
# Test and reload
sudo nginx -t && sudo systemctl reload nginx

Place your SSL certificates at these paths (or adjust the Nginx config):

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

The Nginx config defines three server blocks:

Server blockListens onPurpose
HTTP redirectPort 80Redirects all HTTP traffic to HTTPS
RAMP (HTTPS)Port 443Proxies to RAMP API on localhost:5000
Keycloak (HTTPS)Port 8443Proxies to Keycloak on localhost:8180

Key proxy locations in the RAMP server block:

LocationBackendNotes
/_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
/_healthlocalhost:5000/_healthHealth check (access log disabled)
/localhost:5000/Frontend SPA (catch-all with fallback to index.html)
Static fileslocalhost:50001-year cache, Cache-Control: public, immutable

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.

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

ServiceURLUsernamePassword
RAMPhttps://your-domain.comadminPassw0rd
Keycloakhttps://your-domain.com:8443/adminadminPassw0rd

Terminal window
# RAMP health
curl -s http://localhost:5000/_health
# Keycloak health
curl -s http://localhost:8180/health/ready
# Nginx status
sudo systemctl status nginx

Terminal window
# Check service status
sudo systemctl status ramp
# View detailed logs
sudo journalctl -u ramp -f
# Check application logs
ls -la /var/ramp/logs/

Common causes:

  • Missing appsettings.Production.json
  • Database directory permissions (must be owned by ramp user)
  • .NET runtime not installed
Terminal window
# Check service status
sudo systemctl status keycloak
# View logs
sudo journalctl -u keycloak -f

Common causes:

  • Java 21 not installed
  • Port 8180 already in use
  • Insufficient memory (Keycloak needs ~512 MB)

Cause: RAMP is not running or not listening on localhost:5000.

Terminal window
# Check if RAMP is listening
sudo ss -tlnp | grep 5000
# Restart RAMP
sudo systemctl restart ramp
Terminal window
# Fix ownership
sudo chown -R ramp:ramp /opt/ramp
sudo chown -R ramp:ramp /var/ramp
Terminal window
# Test Nginx configuration
sudo nginx -t
# Check certificate expiry
openssl x509 -in /etc/ssl/your-domain/cert.crt -noout -dates

Terminal window
# Database
cp /var/ramp/data/ramp.db /backups/ramp_$(date +%Y%m%d).db
# Uploaded files
tar czf /backups/ramp_files_$(date +%Y%m%d).tar.gz /var/ramp/data/files/
# Application config
cp /opt/ramp/app/appsettings.Production.json /backups/
Terminal window
# Stop RAMP
sudo systemctl stop ramp
# Restore database
cp /backups/ramp_20260301.db /var/ramp/data/ramp.db
chown ramp:ramp /var/ramp/data/ramp.db
# Start RAMP
sudo systemctl start ramp

  1. Build a new release on your development machine.

  2. Transfer the published files to the server:

    Terminal window
    scp -r src/RAMP.API/publish/* user@your-server:/opt/ramp/app/
  3. 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.


  • 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