132 lines
4.6 KiB
Markdown
132 lines
4.6 KiB
Markdown
|
|
# Deploying raycer to Coolify
|
||
|
|
|
||
|
|
raycer runs in production at **https://raycer.altweb.me** as a Docker
|
||
|
|
Compose application managed by Coolify v4 on the `cool2026` instance.
|
||
|
|
|
||
|
|
## Where things live
|
||
|
|
|
||
|
|
- **Coolify dashboard:** https://cool2026.altweb.me
|
||
|
|
- **Server:** `personal` (Linode 91853095, Amsterdam, 2 GB)
|
||
|
|
- server UUID: `locg048kwko4sws8wcggc0o4`
|
||
|
|
- public IP: `172.235.183.140`
|
||
|
|
- **Project:** `tools` (UUID `u8wooo0wwk4k8wcw48ww8oo8`)
|
||
|
|
- **Source:** Forgejo, `https://forgejo-rko8sk40400wscowk4scko0w.altweb.me/spencer/raycer.git`, branch `main` (mirrored to GitLab `spencerflagg/raycer`)
|
||
|
|
- **Compose file:** `/docker-compose.coolify.yaml` (the local-dev `docker-compose.yml` is for `raycer.test` only and is not used in prod)
|
||
|
|
|
||
|
|
## Containers
|
||
|
|
|
||
|
|
| Service | Image source | Internal port | Public | Memory limit |
|
||
|
|
|----------|-----------------------------|---------------|-------------|--------------|
|
||
|
|
| backend | built from `./backend` | 3000 | (internal) | 256 MiB |
|
||
|
|
| frontend | built from `./frontend` | 80 | raycer.altweb.me | 64 MiB |
|
||
|
|
|
||
|
|
The frontend's nginx reverse-proxies `/api/*` to `http://backend:3000`.
|
||
|
|
|
||
|
|
## Required env (set in Coolify dashboard)
|
||
|
|
|
||
|
|
| Variable | Value | Notes |
|
||
|
|
|-----------------------------|------------------------------------|-------|
|
||
|
|
| `SERVICE_FQDN_FRONTEND_80` | `https://raycer.altweb.me` | Coolify magic var; injects Traefik labels for the frontend service. |
|
||
|
|
|
||
|
|
The backend's `NODE_ENV`, `PORT`, and `DB_PATH` are set inside the compose file.
|
||
|
|
|
||
|
|
## Persistent storage
|
||
|
|
|
||
|
|
A named Docker volume `raycer-data` is mounted into the backend at
|
||
|
|
`/data` and holds `raycer.sqlite`. Coolify creates and manages this
|
||
|
|
volume; it survives redeploys.
|
||
|
|
|
||
|
|
To inspect or back up:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
ssh root@172.235.183.140
|
||
|
|
docker volume inspect <coolify-prefixed-name> # find via: docker volume ls | grep raycer
|
||
|
|
docker run --rm -v <volume>:/data -v $(pwd):/backup alpine \
|
||
|
|
tar czf /backup/raycer-sqlite-$(date +%F).tgz -C /data .
|
||
|
|
```
|
||
|
|
|
||
|
|
## Healthchecks
|
||
|
|
|
||
|
|
Both containers have a `HEALTHCHECK` (backend hits `/api/health`,
|
||
|
|
frontend hits `/`). Coolify's "deployment healthy" gate uses these.
|
||
|
|
|
||
|
|
## DNS
|
||
|
|
|
||
|
|
A record `raycer.altweb.me -> 172.235.183.140` (Linode domain ID
|
||
|
|
`1544692`, TTL 300). To recreate:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
linode-cli domains records-create 1544692 \
|
||
|
|
--type A --name raycer --target 172.235.183.140 --ttl_sec 300
|
||
|
|
```
|
||
|
|
|
||
|
|
## How the app was created
|
||
|
|
|
||
|
|
The Coolify v4 dashboard does not have a great "create from existing
|
||
|
|
config" path, so the original create call hit the API directly:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
TOKEN=$(jq -r '.instances[] | select(.name=="cool2026") | .token' \
|
||
|
|
~/.config/coolify/config.json)
|
||
|
|
|
||
|
|
curl -X POST https://cool2026.altweb.me/api/v1/applications/public \
|
||
|
|
-H "Authorization: Bearer $TOKEN" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{
|
||
|
|
"project_uuid": "u8wooo0wwk4k8wcw48ww8oo8",
|
||
|
|
"server_uuid": "locg048kwko4sws8wcggc0o4",
|
||
|
|
"environment_name": "production",
|
||
|
|
"git_repository": "https://forgejo-rko8sk40400wscowk4scko0w.altweb.me/spencer/raycer.git",
|
||
|
|
"git_branch": "main",
|
||
|
|
"build_pack": "dockercompose",
|
||
|
|
"docker_compose_location": "/docker-compose.coolify.yaml",
|
||
|
|
"name": "raycer",
|
||
|
|
"instant_deploy": false
|
||
|
|
}'
|
||
|
|
```
|
||
|
|
|
||
|
|
After creation, the FQDN env var was set:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST https://cool2026.altweb.me/api/v1/applications/<APP_UUID>/envs \
|
||
|
|
-H "Authorization: Bearer $TOKEN" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"key":"SERVICE_FQDN_FRONTEND_80","value":"https://raycer.altweb.me","is_preview":false}'
|
||
|
|
```
|
||
|
|
|
||
|
|
Then deployed:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl -X POST "https://cool2026.altweb.me/api/v1/deploy?uuid=<APP_UUID>&force=false" \
|
||
|
|
-H "Authorization: Bearer $TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Re-deploy
|
||
|
|
|
||
|
|
Pushing to `main` on Forgejo triggers an automatic redeploy via the
|
||
|
|
Coolify webhook (configured at app create time). To force a manual
|
||
|
|
redeploy:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
coolify deploy <APP_UUID> --context cool2026
|
||
|
|
# or
|
||
|
|
curl -X POST "https://cool2026.altweb.me/api/v1/deploy?uuid=<APP_UUID>" \
|
||
|
|
-H "Authorization: Bearer $TOKEN"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl https://raycer.altweb.me/api/health # {"ok":true,...}
|
||
|
|
curl https://raycer.altweb.me/api/goals # both seeded goals
|
||
|
|
coolify app list --context cool2026 --format json | jq '.[] | select(.name=="raycer")'
|
||
|
|
```
|
||
|
|
|
||
|
|
## Resource notes
|
||
|
|
|
||
|
|
The `personal` server is 2 GB and tightly accounted for; `mem_limit`
|
||
|
|
values in [docker-compose.coolify.yaml](../docker-compose.coolify.yaml)
|
||
|
|
must be respected. Backend's `better-sqlite3` requires a brief native
|
||
|
|
compile during the image build (~10 s, ~100 MB peak); first deploy may
|
||
|
|
take a minute longer than subsequent ones.
|