95 lines
3.9 KiB
Markdown
95 lines
3.9 KiB
Markdown
# Deployment
|
|
|
|
Two independent deployments share the same codebase and Docker image build:
|
|
|
|
| env | container | backend domain | install target | partner-dashboard app | shopify config |
|
|
| ---- | --------------------- | ------------------------------- | ------------------------------------------------ | ----------------------- | ------------------------ |
|
|
| dev | `linumiq-invoice-dev` | `invoice-app-dev.linumiq.com` | `linumiq-dev.myshopify.com` | `linumiq-invoice-dev` | `shopify.app.dev.toml` |
|
|
| prod | `linumiq-invoice-prod`| `invoice-app.linumiq.com` | `5aiizq-ti.myshopify.com` (= `shop.linumiq.com`) | `linumiq-invoice` (prod)| `shopify.app.prod.toml` |
|
|
|
|
## Server layout (root server)
|
|
|
|
```
|
|
/docker/linumiq-invoice/
|
|
├── git/ # checkout of this repo (git pull here)
|
|
├── dev/
|
|
│ ├── docker-compose.yml # symlink → ../git/deploy/docker-compose.dev.yml
|
|
│ ├── .env.dev # secrets (NOT in git)
|
|
│ └── data/ # bind-mounted SQLite + cached assets
|
|
└── prod/
|
|
├── docker-compose.yml # symlink → ../git/deploy/docker-compose.prod.yml
|
|
├── .env.prod # secrets (NOT in git)
|
|
└── data/ # bind-mounted SQLite + cached assets
|
|
```
|
|
|
|
Both containers attach to the external `caddy_net` Docker network. Caddy reverse-proxies each subdomain to the correct container by name (see `Caddyfile.snippet`).
|
|
|
|
## First-time setup on the server
|
|
|
|
```bash
|
|
sudo mkdir -p /docker/linumiq-invoice/{git,dev/data,prod/data}
|
|
sudo chown -R "$USER" /docker/linumiq-invoice
|
|
|
|
cd /docker/linumiq-invoice
|
|
git clone git@git.linumiq.com:LinumIQ/linumiq-invoice.git git
|
|
|
|
# DEV
|
|
cd /docker/linumiq-invoice/dev
|
|
ln -s ../git/deploy/docker-compose.dev.yml docker-compose.yml
|
|
cp ../git/deploy/.env.dev.example .env.dev # then edit secrets
|
|
docker compose up -d --build
|
|
|
|
# PROD
|
|
cd /docker/linumiq-invoice/prod
|
|
ln -s ../git/deploy/docker-compose.prod.yml docker-compose.yml
|
|
cp ../git/deploy/.env.prod.example .env.prod # then edit secrets
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Append `Caddyfile.snippet` to your Caddy config and `docker exec caddy caddy reload --config /etc/caddy/Caddyfile`.
|
|
|
|
## Container runs as a non-root user (uid 1000)
|
|
|
|
The image runs as the unprivileged `node` user (uid/gid **1000**), not root. The
|
|
SQLite database is written to the `/data` bind mount, so the **host** directory
|
|
mounted at `/data` (e.g. `/docker/linumiq-invoice/dev/data` and
|
|
`…/prod/data`) must be writable by uid 1000, otherwise `prisma migrate deploy`
|
|
and DB writes fail on startup:
|
|
|
|
```bash
|
|
sudo chown -R 1000:1000 /docker/linumiq-invoice/dev/data
|
|
sudo chown -R 1000:1000 /docker/linumiq-invoice/prod/data
|
|
```
|
|
|
|
The dev container additionally runs with a **read-only root filesystem**
|
|
(`read_only: true` + `tmpfs: /tmp`), `no-new-privileges`, all Linux capabilities
|
|
dropped, and memory/pids/cpu limits. The app only writes to the `/data` bind
|
|
mount and the tmpfs `/tmp`, so this is safe. (The prod compose is intentionally
|
|
left unchanged.)
|
|
|
|
## Day-to-day redeploy
|
|
|
|
```bash
|
|
cd /docker/linumiq-invoice/git && git pull
|
|
cd /docker/linumiq-invoice/dev && docker compose up -d --build # update dev
|
|
cd /docker/linumiq-invoice/prod && docker compose up -d --build # update prod
|
|
```
|
|
|
|
Run only the env you want to update.
|
|
|
|
## Pushing config / extension changes to Shopify
|
|
|
|
From your dev machine (after `git pull` to keep configs in sync):
|
|
|
|
```bash
|
|
# DEV → linumiq-dev.myshopify.com
|
|
npx shopify app config use shopify.app.dev.toml
|
|
npx shopify app deploy --allow-updates
|
|
|
|
# PROD → shop.linumiq.com
|
|
npx shopify app config use shopify.app.prod.toml
|
|
npx shopify app deploy --allow-updates
|
|
```
|
|
|
|
The currently selected config is stored in `.shopify/project.json` (gitignored), so each developer machine remembers its own choice.
|