# VPS Deployment

Date: 2026-07-22

This project is developed locally and deployed to a fresh VPS. Treat the local machine as the source of truth for source code, Payload database data, and uploaded media.

## Goal

After upload to a new VPS, the site should match the local development state as closely as possible.

That requires three artifacts:

- Source code from GitHub.
- PostgreSQL database dump from the local environment.
- `public/media` files from the local environment.

Source code alone is not enough. Payload content such as pages, globals, products, taxonomy, posts, forms, inquiries, shop settings, and media records lives in PostgreSQL. Uploaded file bytes live in `public/media`.

The repository includes clone-ready backup artifacts for the latest known local state:

- Database: `backups/db/bungamekarsari-data-20260724-091637.sql`
- Media: `backups/media/public-media-20260722-091316.zip`

## Required VPS Environment

Create `.env` on the VPS from `.env.example`. Values are dynamic per server.

Minimum required values:

```env
PAYLOAD_SECRET=replace-with-a-strong-secret
PREVIEW_SECRET=replace-with-a-strong-preview-secret
DATABASE_URL=postgresql://payloadbmj_app:password@127.0.0.1:5432/payloadbmj
NEXT_PUBLIC_SERVER_URL=http://YOUR_VPS_IP_OR_DOMAIN:3000
PAYLOAD_PUBLIC_SERVER_URL=http://YOUR_VPS_IP_OR_DOMAIN:3000
NEXT_ALLOWED_DEV_ORIGINS=YOUR_VPS_IP_OR_DOMAIN
```

Do not define `NEXT_PUBLIC_SERVER_URL` twice. The last duplicate wins and can silently point the app back to `localhost`.

For first VPS bootstrap, if SMTP credentials are not ready:

```env
EMAIL_SKIP_VERIFY=true
```

Remove or review that setting before production email verification.

## Recommended Path: Restore Local State

Use this path when moving the actual developed site to a new VPS.

1. Pull source code:

```bash
git clone https://github.com/villa1/bungamekarsari.git payloadbmj
cd payloadbmj
```

2. Create `.env` with VPS-specific values.

3. Install dependencies:

```bash
pnpm install --frozen-lockfile
```

4. Create the Payload database schema in the empty VPS database:

```bash
pnpm bootstrap:schema
```

5. Restore the PostgreSQL dump included in the repo:

```bash
node scripts/restore-postgres-data.mjs backups/db/bungamekarsari-data-20260724-091637.sql
```

6. Restore the media archive included in the repo:

```bash
rm -rf public/media
unzip -o backups/media/public-media-20260722-091316.zip -d .
```

7. Generate Payload artifacts:

```bash
pnpm generate:types
pnpm generate:importmap
```

8. Build:

```bash
pnpm build
```

9. Start production:

```bash
pnpm start
```

Do not use `pnpm dev` as the normal VPS runtime. `next dev` is for development, compiles routes on demand, uses HMR, and can blank the admin UI over a public IP if dev assets are blocked or the first compile is still warming up.

If you temporarily need to inspect the app on a VPS before production build, use the VPS-specific dev script:

```bash
pnpm dev:vps
```

This keeps the server public on `0.0.0.0`, uses Webpack instead of Turbopack for a more conservative Payload admin development runtime, and disables server fast refresh.

## Fresh Empty Database Path

Use this only for disposable bootstrap or if intentionally starting with empty content.

Do not run `pnpm build` first against an empty database. Some routes query Payload content during build, for example pages and article hub routes.

1. Create `.env`.

2. Install dependencies:

```bash
pnpm install --frozen-lockfile
```

3. Start dev mode once to let Payload create development schema:

```bash
pnpm dev
```

4. Open admin and create the first admin user:

```txt
http://YOUR_VPS_IP_OR_DOMAIN:3000/admin
```

5. Seed or manually create required CMS content.

6. Stop dev server.

7. Generate artifacts and build:

```bash
pnpm generate:types
pnpm generate:importmap
pnpm build
pnpm start
```

This path will not restore the local site state unless the database and media are restored or seeded with equivalent data.

## What Not To Do

Do not run this on production or on a database containing valuable data:

```bash
pnpm payload migrate:fresh
```

This project currently has no `src/migrations` directory. `pnpm payload migrate` will not create the full project state, and `migrate:fresh` is not a substitute for restoring database content.

## Common Failures

### `relation "pages" does not exist`

The VPS database is empty or schema was not created. Restore the local database dump, or run `pnpm dev` once for an intentional fresh bootstrap.

### Admin page is blank in dev mode over IP

First check `.env`. These values must point to the actual VPS IP or domain being opened in the browser:

```env
NEXT_PUBLIC_SERVER_URL=http://YOUR_VPS_IP_OR_DOMAIN:3000
PAYLOAD_PUBLIC_SERVER_URL=http://YOUR_VPS_IP_OR_DOMAIN:3000
NEXT_ALLOWED_DEV_ORIGINS=YOUR_VPS_IP_OR_DOMAIN
```

Do not define `NEXT_PUBLIC_SERVER_URL` or `PAYLOAD_PUBLIC_SERVER_URL` twice. The last duplicate wins and can silently point the app back to `localhost`.

Then restart clean:

```bash
rm -rf .next
pnpm dev:vps
```

If the admin is still blank, check the browser devtools console and Network tab for failed `/_next/...` JavaScript chunks. A server log like `GET /admin 200` only proves the HTML route responded; it does not prove the admin client bundle loaded successfully.

For production verification, prefer:

```bash
rm -rf .next
pnpm build
pnpm start
```

### Media file missing

Database records reference files that were not copied to disk. Sync `public/media` from the local environment.

### Nodemailer `Missing credentials for "PLAIN"`

SMTP env is incomplete. Add Resend/Gmail SMTP credentials, or temporarily set:

```env
EMAIL_SKIP_VERIFY=true
```
