Skip to main content

VPS container deployment

This guide explains how to install and run the complete Payofy stack on a VPS using Docker and a single shell script.

System prerequisites​

Before running the installation script on your server, make sure your VPS has:

  • Ubuntu 22.04 LTS or Debian 12 (recommended).
  • Docker engine version 24 or newer.
  • Docker Compose plugin v2.
  • Git.
  • At least 2 GB RAM and 20 GB free disk space.

Architecture on the VPS​

The VPS runs eight isolated containers on a private Docker bridge network:

  • payofy-postgres-1: PostgreSQL 16 database storing tenants, merchants, and transactions.
  • payofy-redis-1: Redis 7 cache and job queue broker.
  • payofy-api-1: REST API running Fastify on port 3000.
  • payofy-worker-1: Background job worker processing queue tasks.
  • payofy-admin-1: Staff operations web console.
  • payofy-portal-1: Partner and merchant web portal.
  • payofy-docs-1: Developer documentation portal.
  • payofy-gateway-1: Nginx reverse proxy routing traffic on port 80.

Installation steps​

Run these commands in your VPS terminal.

1. Clone the repository​

git clone https://github.com/spicyfy/payofy.git
cd payofy

2. Configure environment variables​

Copy the sample environment file and enter your secrets:

cp .env.example .env
nano .env

Set your database password, Redis credentials, and Kurv API keys.

3. Run the installer script​

You can deploy directly to your VPS from your local computer by providing your VPS IP:

# From your computer (automatically uses username kandikova, port 31973, and ~/.ssh/id_ed25519)
./scripts/deploy/vps-install.sh "app.payofy.si"

# Or with an IP address:
./scripts/deploy/vps-install.sh "198.51.100.1"

If you are already logged in to the VPS terminal:

# Directly on the VPS terminal
./scripts/deploy/vps-install.sh "198.51.100.1"

The script automatically:

  • Tests SSH connectivity and verifies or installs Docker engine, Node.js, and PM2.
  • Syncs the codebase to ~/payofy on the remote server.
  • Builds Docker images for all applications.
  • Starts PostgreSQL, Redis, backend services, web consoles, and the Nginx gateway.
  • Waits for database readiness.
  • Runs database migrations.
  • Seeds initial tenant and merchant records.
  • Configures PM2 log streaming for all services.
  • Prints service health checks and active endpoints.

Service endpoints​

When the script finishes, your services are reachable at these URLs:

  • Nginx Gateway: http://<your-vps-ip>/ and https://<your-vps-ip>/
  • API Root: https://api.payofy.si/v1/health (or http://api.payofy.si/v1/health)
  • Admin Console: https://admin.payofy.si (or http://admin.payofy.si)
  • Partner Portal: https://portal.payofy.si (or http://portal.payofy.si)
  • Documentation: https://docs.payofy.si (or http://docs.payofy.si)

SSL and HTTPS configuration​

Payofy supports free Let's Encrypt SSL certificates for HTTPS access on port 443.

DNS verification​

Before issuing certificates, make sure your domain A records resolve to your VPS:

  • api.payofy.si points to your VPS IP
  • admin.payofy.si points to your VPS IP
  • docs.payofy.si points to your VPS IP
  • portal.payofy.si points to your VPS IP
  • app.payofy.si points to your VPS IP

Running the SSL setup script​

Run this script on the VPS terminal:

./scripts/setup/ssl-setup.sh admin@payofy.si

The script:

  • Verifies that each domain resolves to the VPS IP to avoid Let's Encrypt challenge errors.
  • Issues a certificate via Certbot using HTTP webroot mode without stopping containers.
  • Installs the certificates into ./docker/ssl/.
  • Gracefully reloads the Nginx gateway.

Portal security and authentication​

Both the Ops Admin Console and Partner Portal include in-app authentication, session protection, and gateway-level security headers.

Default credentials​

  • Ops Admin Console (https://admin.payofy.si):
    • Email: admin@payofy.si
    • Passcode: PayofyStaff2026!
    • Security: 5 failed attempts trigger a 60-second lockout. Sessions expire upon closing the browser window.
  • Partner Portal (https://portal.payofy.si):
    • Email option: partner@budofy.si / BudofyPartner2026!
    • API Key option: Accepts any tenant secret key starting with pfy_live_ or pfy_test_.

Gateway security protections​

The Nginx gateway enforces the following settings:

  • Frame protection: X-Frame-Options: SAMEORIGIN blocks iframe clickjacking.
  • MIME protection: X-Content-Type-Options: nosniff stops MIME type sniffing.
  • Transport security: Strict-Transport-Security enforces HTTPS across all subdomains.
  • Rate limiting: 25 requests per second on portal routes and 30 requests per second on API routes.
  • Version hiding: server_tokens off prevents exposing Nginx version numbers.

Checking logs with PM2​

PM2 runs on the VPS host to stream and manage logs across all containers.

Common PM2 log commands​

View live logs from all services at once:

pm2 logs

Stream logs from a specific service:

# REST API logs
pm2 logs payofy-api

# Background queue worker logs
pm2 logs payofy-worker

# Nginx reverse proxy logs
pm2 logs payofy-gateway

# Database logs
pm2 logs payofy-postgres

# Redis cache logs
pm2 logs payofy-redis

Display only the last 50 lines without continuous streaming:

pm2 logs payofy-api --lines 50 --nostream

Check status and memory usage of all log streams:

pm2 status

Open the interactive live terminal dashboard:

pm2 monit

Clear historical log files:

pm2 flush

Starting and stopping PM2​

Start all log stream processes:

pm2 start ecosystem.config.js

Restart log streamers:

pm2 restart ecosystem.config.js

Stop log streamers:

pm2 stop ecosystem.config.js

You can also use the ops script from the repository root:

./scripts/ops/logs.sh api
./scripts/ops/logs.sh worker
./scripts/ops/logs.sh monit

Docker operations commands​

Check running containers:

docker compose ps

Restart all services:

docker compose restart

Stop all services:

docker compose down