Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.git
.github
.next
node_modules
docs
mcp-server
supabase
*.md
!README.md
.env*
!.env.local.example
Dockerfile
docker-compose*.yml
.dockerignore
.vscode
.idea
coverage
55 changes: 55 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# syntax=docker/dockerfile:1

# ---------------------------------------------------------------
# Stage 1 — install dependencies (cached until package*.json change)
# ---------------------------------------------------------------
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# ---------------------------------------------------------------
# Stage 2 — build
#
# NEXT_PUBLIC_* values are inlined into the client bundle at build
# time, so they must be provided as build args (docker-compose.yml
# forwards them from .env.local). Server-only secrets (service role
# key, ENCRYPTION_KEY, META_APP_SECRET, ...) are read at runtime and
# must NOT be baked into the image.
# ---------------------------------------------------------------
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
ARG NEXT_PUBLIC_SITE_URL
ARG NEXT_PUBLIC_APP_LOCALE=en
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL \
NEXT_PUBLIC_APP_LOCALE=$NEXT_PUBLIC_APP_LOCALE \
NEXT_TELEMETRY_DISABLED=1

RUN npm run build

# ---------------------------------------------------------------
# Stage 3 — minimal runtime (standalone output)
# ---------------------------------------------------------------
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=3000 \
HOSTNAME=0.0.0.0

RUN addgroup -S nextjs && adduser -S nextjs -G nextjs

COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nextjs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nextjs /app/public ./public

USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ npm run dev
Open <http://localhost:3000>. You'll be redirected to `/login` (or
`/dashboard` if already signed in).

Prefer containers? See [docs/docker.md](./docs/docker.md) for the
Dockerfile + Docker Compose setup.

## 🚀 Deploy on Hostinger (recommended)

<p align="center">
Expand Down
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: wacrm

services:
app:
build:
context: .
# NEXT_PUBLIC_* vars are inlined at build time — forward them
# from .env.local as build args. Changing any of them requires
# a rebuild (docker compose up --build).
args:
NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY}
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-}
NEXT_PUBLIC_APP_LOCALE: ${NEXT_PUBLIC_APP_LOCALE:-en}
# Runtime (server-only) secrets come from .env.local; they are
# never baked into the image.
env_file:
- .env.local
ports:
- "${PORT:-3000}:3000"
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://localhost:3000').then((r)=>process.exit(r.ok||r.status<500?0:1)).catch(()=>process.exit(1))",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
57 changes: 57 additions & 0 deletions docs/docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Running with Docker

The repo ships a multi-stage `Dockerfile` (Next.js standalone output,
runs as a non-root user) and a `docker-compose.yml` with a single
`app` service. Supabase is external — point the app at your hosted
(or self-hosted) Supabase project via env vars; no database container
is included.

## Quick start

1. Copy the env template and fill it in:

```bash
cp .env.local.example .env.local
```

2. Build and start (the `--env-file` flag is required — Compose only
reads `.env` by default for `${VAR}` substitution, and this project
keeps its config in `.env.local`):

```bash
docker compose --env-file .env.local up --build -d
```

3. The app is served on [http://localhost:3000](http://localhost:3000)
(override the host port with `PORT=8080` in `.env.local`).

## Build-time vs runtime variables

- `NEXT_PUBLIC_*` variables are **inlined into the client bundle at
build time**. They are passed as Docker build args by
`docker-compose.yml`. If you change any of them, rebuild:
`docker compose --env-file .env.local up --build -d`.
- Everything else (`SUPABASE_SERVICE_ROLE_KEY`, `ENCRYPTION_KEY`,
`META_APP_SECRET`, …) is read at **runtime** from `.env.local` via
`env_file` and is never baked into the image — safe to change with
just a container restart.

## Plain Docker (no Compose)

```bash
docker build \
--build-arg NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co \
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \
-t wacrm .

docker run -d --env-file .env.local -p 3000:3000 wacrm
```

## Notes

- Database migrations under `supabase/` are **not** run by the
container — apply them with the Supabase CLI as described in the
README.
- If you use automation Wait steps, remember to point your cron pinger
at `GET /api/automations/cron` on this deployment (see
`docs/automations-and-cron.md`).
5 changes: 5 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const SECURITY_HEADERS = [
] as const;

const nextConfig: NextConfig = {
// Emit a self-contained server bundle (.next/standalone) so the
// Docker image can run without node_modules or the Next CLI.
// Harmless outside Docker: `next start` keeps working as before.
output: "standalone",

/**
* Cache-Control policy.
*
Expand Down