> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nudgra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy on a VPS

> Deploy Nudgra OSS with Docker Compose, Postgres, and Caddy.

This is the recommended production path for Nudgra OSS. You host the app and Postgres on a VPS, then route HTTPS traffic through Caddy.

## Prerequisites

Before you start, prepare:

* A VPS running Ubuntu or Debian.
* A domain or subdomain.
* DNS access for the domain.
* Google OAuth credentials.
* Meta app credentials and a webhook verify token.

## Point DNS to the VPS

Create an `A` record for the domain or subdomain.

```text theme={null}
Type: A
Name: nudgra
Value: YOUR_VPS_PUBLIC_IP
Proxy status: DNS only while issuing the first certificate
```

If you use Cloudflare, use **DNS only** for the first certificate setup. After HTTPS works, you can enable the proxy if SSL/TLS mode is **Full strict**.

## Install server packages

On Ubuntu or Debian:

```bash theme={null}
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl ufw
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
```

Install Caddy with your preferred package method. If Caddy already serves other apps on the server, keep it running and add a new site block for Nudgra.

Open the firewall:

```bash theme={null}
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
```

## Clone the app

```bash theme={null}
cd /opt
sudo git clone https://github.com/MaikoCode/nudgra-oss nudgra
sudo chown -R $USER:$USER /opt/nudgra
cd /opt/nudgra
cp .env.example .env
```

## Configure production `.env`

Edit:

```bash theme={null}
nano .env
```

Set:

```env theme={null}
SITE_URL=https://your-domain.com
BETTER_AUTH_URL=https://your-domain.com
TRUSTED_ORIGINS=https://your-domain.com

BETTER_AUTH_SECRET=your-generated-secret
TOKEN_ENCRYPTION_KEY=your-generated-token-secret

OPERATOR_EMAIL_ALLOWLIST=your-google-email@gmail.com

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

META_APP_ID=your-instagram-app-id
META_APP_SECRET=your-instagram-app-secret
META_VERIFY_TOKEN=your-generated-webhook-verify-token
```

<Warning>
  Keep a private copy of `.env`. It contains the auth and token-encryption secrets needed to decrypt stored OAuth and Instagram tokens.
</Warning>

For production, also change the Postgres password in `docker-compose.yml` and update `DATABASE_URL` inside `app.environment` to match.

Recommended: bind the app port to localhost only:

```yaml theme={null}
ports:
  - "127.0.0.1:3000:3000"
```

## Start the app

```bash theme={null}
docker compose up -d --build
docker compose logs -f app
```

The app container runs database migrations before startup.

Check the local health endpoint:

```bash theme={null}
curl http://127.0.0.1:3000/api/v1/health
```

## Configure Caddy

Edit:

```bash theme={null}
sudo nano /etc/caddy/Caddyfile
```

Add a site block:

```caddy theme={null}
your-domain.com {
    reverse_proxy 127.0.0.1:3000
}
```

If your Caddy server is already bound to a specific public IP for other apps, add `bind`:

```caddy theme={null}
your-domain.com {
    bind YOUR_VPS_PUBLIC_IP
    reverse_proxy 127.0.0.1:3000
}
```

Validate and reload:

```bash theme={null}
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```

Check HTTPS:

```bash theme={null}
curl -I https://your-domain.com/api/v1/health
```

You want a `200` response.

## Update a deployment

```bash theme={null}
cd /opt/nudgra
git pull
docker compose up -d --build
docker compose logs -f app
```

## Back up Postgres

Create a compressed backup:

```bash theme={null}
docker compose exec -T postgres pg_dump -U nudgra -d nudgra --format=custom > nudgra.backup
```

Restore into an empty database:

```bash theme={null}
docker compose exec -T postgres pg_restore -U nudgra -d nudgra --clean --if-exists < nudgra.backup
```

Back up `.env` separately.
