# Virtual Machine (Docker)

Deploy FormSG to a virtual machine for small-scale production use, staging environments, or pilot deployments. This guide uses Docker Compose on a single server to get FormSG running with minimal infrastructure complexity.

### Overview

This deployment is ideal for:

* **Pilot projects** - Test FormSG with real users before full production
* **Small organizations** - Up to 1,000 form submissions per month
* **Staging environments** - Test customizations and integrations
* **Air-gapped deployments** - Isolated government networks
* **Budget-conscious teams** - Simple infrastructure with predictable costs

### Prerequisites

* Cloud provider account (AWS, Azure, GCP) or physical server access
* Basic Linux command line knowledge
* SSH client on your local machine
* Domain name (optional but recommended)

### Architecture Overview

<figure><img src="/files/UMyV8zlwPegdTSPLfR3R" alt=""><figcaption></figcaption></figure>

This VM deployment runs FormSG components on a single server using docker containers, specific services can be seen in docker config [here](https://github.com/opengovsg/FormSG/blob/develop/docker-compose.yml).

### Deployment Steps

{% stepper %}
{% step %}

#### Create Virtual Machine

**Cloud Provider Setup:**

Choose your preferred cloud provider and create a new VM instance:

* **AWS**: EC2 → Launch Instance → Ubuntu 22.04 LTS
* **Azure**: Virtual Machines → Create → Ubuntu 22.04 LTS
* **GCP**: Compute Engine → VM instances → Create → Ubuntu 22.04 LTS

**Recommended Specifications:**

* **CPU**: 2 vCPUs (minimum 1 vCPU for testing)
* **Memory**: 4 GB RAM (minimum 2 GB)
* **Storage**: Few GBs, depending on need
* **Network**: Public IP address if external access needed

**Instance Examples:**

* **AWS**: t3.small or t3.medium (t2.micro for testing)
* **Azure**: Standard\_B2s or Standard\_B1ms (Standard\_B1s for testing)
* **GCP**: e2-small or e2-medium (e2-micro for testing)

**Security Group/Firewall Rules:**

* SSH (port 22) from your IP address
* HTTP (port 80) from anywhere (if using domain)
* HTTPS (port 443) from anywhere (if using SSL)
* Custom port 5173 from anywhere (for direct access)

{% hint style="warning" %}
**Security Note**: Restrict SSH access to your IP address only. Consider using a VPN or bastion host for production deployments.
{% endhint %}
{% endstep %}

{% step %}

#### Connect to Your VM

SSH into your newly created virtual machine:

```bash
# Replace with your VM's public IP address
ssh ubuntu@YOUR_VM_IP_ADDRESS

# If using SSH key authentication (recommended)
ssh -i /path/to/your-key.pem ubuntu@YOUR_VM_IP_ADDRESS
```

**First Time Setup:**

```bash
# Update system packages
sudo apt update && sudo apt upgrade -y

# Install essential tools (adjust based on your VM setup)
sudo apt install -y curl wget git htop unzip
```

{% hint style="info" %}
**Additional tools**: Depending on your VM setup, you may want to install other utilities like `wget`, `htop`, `unzip`, `nano`, or `vim`. Install what you need for your environment.
{% endhint %}
{% endstep %}

{% step %}

#### Install Docker and Docker Compose

This deployment path requires Docker and Docker Compose to run. Install them using your preferred method:

**Option 1: Official Docker Installation (Recommended)**

Follow the official Docker installation guide for Ubuntu:

* Visit: <https://docs.docker.com/engine/install/ubuntu/>
* Ensure you install Docker Compose plugin

**Option 2: Quick Installation via Convenience Script**

```bash
# Download and run Docker's convenience script
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Install Docker Compose plugin
sudo apt-get update
sudo apt-get install docker-compose-plugin
```

**Option 3: Package Manager Installation**

```bash
# Install Docker from Ubuntu repositories (may be older version)
sudo apt update
sudo apt install docker.io docker-compose-v2
```

**Post-Installation Setup:**

```bash
# Add your user to docker group (avoids using sudo)
sudo usermod -aG docker $USER

# Apply group changes
newgrp docker

# Verify installation
docker --version
docker compose version
docker run hello-world
```

{% hint style="info" %}
**Note**: You may need to log out and back in for group changes to take effect. The convenience script (Option 2) is often the easiest for most usecase.
{% endhint %}
{% endstep %}

{% step %}

#### Clone FormSG Repository

Get the FormSG source code:

```bash
# Clone FormSG repository
git clone https://github.com/opengovsg/FormSG.git
cd FormSG

# Install Node.js (using NodeSource repository)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verify Node.js version
node --version  # Should show v22.x.x
```

{% endstep %}

{% step %}

#### Configure Environment

Set up FormSG configuration for your VM:

```bash
# Copy example environment file
cp .env.example .env

# Edit environment file
nano .env
```

**Essential Configuration Changes:**

Update these variables in your `.env` file:

```bash
# Email Configuration (required for OTP login)
# Option 1: Use a simple SMTP service
SES_HOST=smtp.gmail.com
SES_PORT=587
SES_USER=your-email@gmail.com
SES_PASS=your-app-password
MAIL_FROM=noreply@your-domain.com

# Option 2: Use your organization's SMTP
SES_HOST=mail.yourorg.gov
SES_PORT=587
SES_USER=formsg-service
SES_PASS=your-smtp-password
MAIL_FROM=formsg@yourorg.gov


# Database (MongoDB will run in Docker)
DB_HOST=mongodb://mongo:27017/formsg
```

{% endstep %}

{% step %}

#### Install Dependencies and Build

Install FormSG dependencies:

```bash
# Install npm packages
npm install && npm --prefix serverless/virus-scanner install

# Build frontend for production
npm run build:frontend

# Verify build completed
ls -la dist/frontend
```

This step may take 5-10 minutes depending on your VM's specs and network speed.
{% endstep %}

{% step %}

#### Start FormSG Services

You have two options for starting FormSG:

**Option 1: All-in-One (Recommended)**

```bash
# Starts both frontend and backend services (via docker) together
npm run dev
```

This handles both the frontend build and Docker services automatically.

**Option 2: Backend Only**

```bash
# Start backend services only
docker compose up -d

# Then start frontend separately (in another terminal/tmux ession)
npm run dev:frontend
```

Use this if you want more control over individual services.

**Services Starting:**

* **MongoDB**: Database server
* **MailDev**: Email testing
* **FormSG Backend**: API server
* **FormSG Frontend**: Web interface (port 5173)

{% hint style="info" %}
**Recommended**: Use `npm run dev` for simplicity. It handles everything you need for a VM deployment.
{% endhint %}
{% endstep %}
{% endstepper %}

### Accessing Your FormSG Instance

#### Web Interface

Open your browser and navigate to:

* **Main Application**: `http://YOUR_VM_IP:5173`
* **Email Testing**: `http://YOUR_VM_IP:1080` (MailDev interface)

#### First Login

1. Go to the admin portal: `http://YOUR_VM_IP:5173/login`
2. Click "Login with Email"
3. Enter your email address
4. Check the MailDev interface (`http://YOUR_VM_IP:1080`) for the OTP code
5. Enter the OTP to complete login

### Scaling

This VM deployment runs all FormSG components on a single server, which limits scaling options. For high-traffic scenarios or high availability requirements, consider moving to a cloud-native deployment with multiple servers.

#### When to Move Beyond VM Deployment

Consider migrating to a more robust deployment when you experience:

* **High traffic**: >1,000 concurrent users or >10,000 daily submissions
* **Availability requirements**: Need 99.9% uptime or zero-downtime deployments
* **Advanced integrations**: Complex workflows requiring multiple services

***

{% hint style="success" %}
**🎉 Congratulations!** You now have FormSG running on a virtual machine. This provides a solid foundation for small-scale production use while maintaining full control over your infrastructure.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://international.open.gov.sg/self-hosting/formsg/deployment/virtual-machine-docker.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
