Explita InfraExplita Infra
Deploy

Host Server Setup

One-line automated provisioning or detailed manual step-by-step setup for Explita Deploy

Host Server Setup & Provisioning

To deploy applications with eda, you need an active Explita Deploy host server. This guide covers how to provision a fresh Linux VPS (Ubuntu, Debian, Fedora, or Rocky Linux) using either our automated one-line installer or a detailed manual step-by-step installation, followed by configuring your local developer machine.


Prerequisites

Before beginning, ensure your host server satisfies the following:

  • Operating System: 64-bit Linux VPS or dedicated server (Ubuntu 22.04/24.04 LTS recommended, Debian 12, or Fedora/RHEL).
  • Access: root or a user with passwordless sudo privileges.
  • Hardware: Minimum 1 GB RAM (2 GB+ recommended for running Docker builds and database containers).
  • Open Ports:
    • 80 (HTTP) — Inbound web traffic and Let's Encrypt ACME challenges.
    • 443 (HTTPS) — Inbound encrypted production web traffic.
    • 39756 (TCP) — Explita Deploy control plane API (can be accessed directly or reverse-proxied under a custom domain).

The automated script configures Docker Engine, Node.js 24 LTS, pnpm, PM2, Caddy, managed PostgreSQL, persistent storage directories, and generates secure cryptographic keys. It is completely idempotent—safe to run on fresh servers or rerun anytime to pull updates without touching existing databases.

Run the One-Liner

SSH into your server and run:

curl -fsSL https://raw.githubusercontent.com/explita/deploy-server/main/scripts/setup.sh | sudo bash

Optional Environment Overrides

You can customize the installation directory, branch, or repository by passing environment variables:

# Example: Deploying from a specific branch or custom install directory
INSTALL_DIR=/opt/explita/deploy BRANCH=main sudo -E bash -c \
  "$(curl -fsSL https://raw.githubusercontent.com/explita/deploy-server/main/scripts/setup.sh)"

What the One-Liner Automatically Does

  1. System Packages: Updates package repos and installs curl, git, openssl, tar, and gzip.
  2. Docker Engine: Installs official Docker Engine, enables the docker service, adds your user to the docker group, and creates the internal bridge network explita-net.
  3. Node.js & Tooling: Installs Node.js 24 LTS, pnpm 12, and the PM2 process manager globally.
  4. Storage Hierarchy: Creates persistent host directories under /var/lib/explita/ with locked-down permissions:
    • /var/lib/explita/postgres (database data)
    • /var/lib/explita/redis (in-memory state)
    • /var/lib/explita/caddy/apps & /var/lib/explita/caddy/data (reverse proxy configs and TLS certificates)
    • /var/lib/explita/backups/databases (SQL snapshot archives)
    • /var/lib/explita/deployments (source archives and active releases)
  5. Infrastructure Containers:
    • Launches explita-caddy with dynamic API enabled on http://127.0.0.1:2019.
    • Launches explita-postgres on explita-net with persistent storage.
  6. Configuration (.env): Generates random cryptographically secure keys:
    • APP_ENCRYPTION_KEY: 64-character hex key for encrypting environment variables.
    • DEPLOY_TOKEN: Secret authentication bearer token required by the CLI.
    • POSTGRES_ROOT_PASSWORD: Secure root credentials for the managed database. (See .env.example for all optional server settings).
  7. Database Sync & Build: Runs prisma db push to initialize SQLite/PostgreSQL internal schemas, builds the TypeScript server, and launches it under PM2 with system boot persistence.

Once completed, the script prints your DEPLOY_TOKEN and instructions on how to bind your local CLI.


Option 2: Detailed Manual Step-by-Step Setup

If you prefer to configure each component manually or use customized orchestration, follow this step-by-step process.

Step 1: Install Base Utilities

sudo apt-get update && sudo apt-get install -y curl git openssl tar gzip

Step 2: Install Docker Engine & Create Network

Install Docker using the official automated installer:

curl -fsSL https://get.docker.com | sudo sh
sudo systemctl enable --now docker

# Add your current user to the docker group
sudo usermod -aG docker $USER

# Activate docker group in current shell (or reconnect via SSH)
newgrp docker

# Create the dedicated Docker bridge network for Explita
docker network create explita-net

Step 3: Install Node.js 24 LTS, pnpm & PM2

# Install Node.js 24 LTS via NodeSource
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs

# Install pnpm 12 and PM2 globally
sudo npm install -g pnpm@12 pm2

Step 4: Create Host Storage Directories & Assign Ownership

Create the required persistence directories under /var/lib/explita:

sudo mkdir -p /var/lib/explita/postgres \
             /var/lib/explita/redis \
             /var/lib/explita/caddy/apps \
             /var/lib/explita/caddy/data \
             /var/lib/explita/backups/databases \
             /var/lib/explita/deployments \
             /var/lib/explita/custom/files \
             /var/lib/explita/custom/caddy

# Assign ownership to your service user (allows PM2 to write Caddy configs and archives)
sudo chown -R $USER:$(id -gn $USER) \
  /var/lib/explita/deployments \
  /var/lib/explita/caddy \
  /var/lib/explita/backups \
  /var/lib/explita/custom

# Assign security permissions
sudo chmod 755 /var/lib/explita
sudo chmod 755 /var/lib/explita/deployments
sudo chmod 755 /var/lib/explita/caddy
sudo chmod 755 /var/lib/explita/caddy/apps
sudo chmod 755 /var/lib/explita/caddy/data
sudo chmod 700 /var/lib/explita/backups
sudo chmod 700 /var/lib/explita/backups/databases
sudo chmod 755 /var/lib/explita/custom
sudo chmod 755 /var/lib/explita/custom/files
sudo chmod 755 /var/lib/explita/custom/caddy
sudo chmod 755 /var/lib/explita/postgres
sudo chmod 755 /var/lib/explita/redis

Step 5: Clone Control Plane & Configure Environment

Clone the repository to /opt/explita/deploy:

sudo mkdir -p /opt/explita
sudo git clone --depth 1 -b main https://github.com/explita/deploy-server.git /opt/explita/deploy
cd /opt/explita/deploy
sudo chown -R $USER:$(id -gn $USER) /opt/explita/deploy

Generate production cryptographic keys and create .env:

if [ -f .env ]; then
  echo "Error: .env file already exists. Aborting to protect existing keys and data!"
else
  ENCRYPTION_KEY=$(openssl rand -hex 32)
  DEPLOY_TOKEN=$(openssl rand -hex 24)
  POSTGRES_PASS=$(openssl rand -hex 24)

  cat <<EOF > .env
APP_ENCRYPTION_KEY=${ENCRYPTION_KEY}
DATA_DIR=/var/lib/explita
POSTGRES_ROOT_PASSWORD=${POSTGRES_PASS}
PORT=39756
HOST=0.0.0.0
DEPLOY_TOKEN=${DEPLOY_TOKEN}
LOG_INFO=false
MAX_REQUEST_BODY_SIZE=100MB
IMAGE_RETENTION_LIMIT=3
CRON_HISTORY_LIMIT=50
REDIS_ENABLED=false
TUNNEL_ENABLED=true
EOF

  chmod 600 .env
  echo ".env created successfully."
fi

[!NOTE] Save your DEPLOY_TOKEN and POSTGRES_ROOT_PASSWORD in a secure password manager. The DEPLOY_TOKEN is required by developers to authenticate their CLI with this host.

[!CAUTION] Do NOT re-generate or overwrite .env after initial setup.

  • Changing APP_ENCRYPTION_KEY will permanently prevent decryption of existing secrets and credentials stored in the database.
  • Changing POSTGRES_ROOT_PASSWORD will cause database connection failures because PostgreSQL's data volume (/var/lib/explita/postgres) is initialized once with the original password.
  • Changing DEPLOY_TOKEN will immediately invalidate all developer CLI connections and CI/CD pipelines.

[!TIP] Full Environment Variable Reference: For the complete list of server options—including optional 2FA (DEPLOY_2FA_SECRET), static PINs (DEPLOY_PASSCODE), server-wide S3 off-site backups (GLOBAL_S3_*), custom ports, and Redis authentication—see the fully annotated .env.example file in the repository.

Step 6: Build & Launch Control Plane with PM2

Install dependencies and compile the TypeScript control plane:

# Install dependencies
pnpm install

# Build TypeScript server
pnpm build

# Optional: Auto-provision preview tunnel token (for branch preview URLs)
sudo bash scripts/setup-tunnel.sh --auto

# Launch server with PM2 using production ecosystem config
pm2 start ecosystem.config.cjs
pm2 save

# Configure PM2 systemd startup for reboot persistence
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u $USER --hp $HOME

[!IMPORTANT] Automatic Infrastructure Provisioning: When the control plane boots, it automatically provisions the explita-caddy reverse proxy container on ports 80 and 443, initializes the managed postgres container on explita-net, synchronizes internal database schemas, and starts background schedulers. You do not need to run docker run for Caddy or PostgreSQL manually.

Step 7: Configure Host Firewall & Cloud Security Groups

Depending on your VPS distribution and cloud provider, configure network ingress:

1. Host Firewall (iptables / UFW)

If your server uses raw iptables (default on Oracle Cloud and minimal Linux distros) or UFW:

# For iptables (Oracle Cloud, Debian, minimal distros)
sudo iptables -I INPUT 1 -p tcp --dport 39756 -j ACCEPT
sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT 1 -p tcp --dport 443 -j ACCEPT

# Persist iptables rules across reboots (if netfilter-persistent is installed)
sudo netfilter-persistent save 2>/dev/null || true

# If UFW is installed and active
if command -v ufw >/dev/null 2>&1 && sudo ufw status | grep -qw "active"; then
  sudo ufw allow 39756/tcp comment "Explita Deploy API"
  sudo ufw allow 80/tcp comment "Explita HTTP / ACME"
  sudo ufw allow 443/tcp comment "Explita HTTPS / TLS"
  sudo ufw reload
fi

2. Cloud Provider Ingress Rules (AWS, Oracle Cloud, GCP, Azure)

Opening host ports is not enough if your cloud provider enforces an external virtual firewall (Security Groups / VCN Security Lists). Ensure inbound rules allow:

  • 39756/tcp: Explita Deploy control plane API
  • 80/tcp: HTTP and automatic ACME SSL certificate challenges
  • 443/tcp: HTTPS traffic for deployed applications

[!TIP] On Oracle Cloud (OCI), navigate to:
Networking > Virtual Cloud Networks > [Your VCN] > Security Lists > Default Security List > Add Ingress Rules
Add an ingress rule with Source CIDR: 0.0.0.0/0, IP Protocol: TCP, and Destination Port Range: 39756, 80, 443.

Step 8: Verify Service Health

[!NOTE] First Run Startup Delay (30–90 seconds): On the very first startup, Explita Deploy pulls the required caddy:2-alpine and postgres:17-alpine Docker images, waits for PostgreSQL to become fully ready, and automatically applies Prisma database migrations.

If curl does not respond immediately, monitor initialization progress in real time:

pm2 logs explita-deploy

Verify that the Explita Deploy control plane is healthy and responding:

curl -fsS http://127.0.0.1:39756/health

Expected output:

{ "ok": true, "service": "explita-deploy", "version": "0.1.0" }

Configuring the Local Developer CLI (eda)

Now that your host server is running, configure your local workstation to deploy code to it.

1. Install the CLI Globally

Install @explita/cli via your preferred package manager:

pnpm add -g @explita/cli
# or: npm install -g @explita/cli
# or: yarn global add @explita/cli

Verify the installation:

eda --version

2. Connect CLI to Your Remote Server

Configure your server endpoint and deployment token:

# Set your server address (IP with port 39756 or custom reverse-proxy domain)
eda config set server http://<your-server-ip>:39756

# Set your authentication token (the DEPLOY_TOKEN from your server's .env)
eda config set token <your-deploy-token>

Verify your configuration:

eda config ls

3. (Optional) Static PIN / Passcode Protection

If you configured DEPLOY_PASSCODE in your server /opt/explita/deploy/.env for two-factor protection, passcodes are strictly forbidden from being saved to configuration files (you only know them in your head).

When running sensitive or administrative commands, the CLI interactively prompts for your PIN:

🔑 Enter deploy passcode / PIN:

Or you can pass it inline in memory for that single command invocation without saving it to disk:

eda promote <deploymentId> --pass <secret-pin>

4. Run Pre-Flight Diagnostics (eda doctor)

Run eda doctor to verify server connectivity, Docker status, Caddy proxy health, and disk space:

eda doctor

Sample successful output:

✓ Control Plane API: Connected (24ms latency)
✓ Authentication: Valid bearer token
✓ Remote Docker: Docker Engine v27.3.1 active (2 running containers)
✓ Host Disk Space: 42.1 GB free (18% used)
✓ Caddy Reverse Proxy: Operational (Dynamic API reachable)
✓ System Health: All systems ready for deployment

You are now ready to deploy your first application! Proceed to Deploying Your First App.

On this page