Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
8 changes: 7 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFz
VITE_IS_DEMO=false
VITE_INBOUND_EMAIL=2aff30e603e54dc3eb556bd9e03ee099@inbound.postmarkapp.com
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:54322/postgres
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:54322/postgres

## For self host with Docker
# FRONTEND_HTTP_HOST=true
# FRONTEND_HTTP_PORT=3000
# FRONTEND_HMR_PORT=3001
# VITE_SUPABASE_URL=http://localhost:8000
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ dist/

# Shadcn Registry
public/r/
docker/*
!docker/docker-compose-atomic.yml
!docker/Dockerfile
45 changes: 45 additions & 0 deletions doc/src/content/docs/developers/self-hosting.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: Self Hosting
description: This document explains how to host Atomic CRM and Supabase yourself.
---

In order to facilitate self-hosting Atomic CRM with Supabase, we provide a few Make commands.

First, initialize the docker setup by running:

```sh
make docker-init
```

This command will ask you to allow writing an `./docker/.env` file for Supabase. When it's done, feel free to customize this file. Refer to the [Supabase documentation](https://supabase.com/docs/guides/self-hosting/docker#configuring-and-securing-supabase) for details about each variable.

## Development

Once this is done, you can run the following command to start the development environment:

```sh
make docker-start

@WiXSL WiXSL Jan 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't start everything without setting these params first:

FRONTEND_HTTP_HOST=true
FRONTEND_HTTP_PORT=3000
FRONTEND_HMR_PORT=3001

make docker-start output:

docker compose -p atomic-crm --env-file .env.development --env-file ./docker/.env -f ./docker/docker-compose.yml -f ./docker/docker-compose-atomic.yml up
WARN[0000] The "FRONTEND_HMR_PORT" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HTTP_HOST" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HTTP_PORT" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HTTP_PORT" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HTTP_PORT" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HMR_PORT" variable is not set. Defaulting to a blank string.
WARN[0000] The "FRONTEND_HMR_PORT" variable is not set. Defaulting to a blank string.
invalid proto:
make: *** [makefile:102: docker-start] Error 1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's explained in the self hosting documentation

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️ you are right

```

To setup the database and edge functions required by Atomic CRM migrations, run the following command, replacing the `[POOLER_TENANT_ID]` and `[POSTGRES_PASSWORD]` placeholders with the values from the Supabase `./docker/.env` file:

```sh
PGSSLMODE=disable npx supabase db push --db-url "postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain]:5432/postgres"
```

`PGSSLMODE=disable` is required to access the local postgres instance.

Finally, update the frontend application `.env` file to target the local supabase instance:

```sh
FRONTEND_HTTP_HOST=true
FRONTEND_HTTP_PORT=3000
FRONTEND_HMR_PORT=3001
VITE_SUPABASE_URL=http://localhost:8000
VITE_SUPABASE_ANON_KEY=Put the ANON_KEY value from ./docker/.env
SUPABASE_SERVICE_ROLE_KEY=Put the SERVICE_ROLE_KEY value from ./docker/.env
```

Replace the port of the `VITE_SUPABASE_URL` variable with the one you choose for `SUPABASE_PUBLIC_URL` in the Supabase `./docker/.env` file.

If you edit any value in either the frontend `.env` or `./docker/.env` files, you'll have to restart the docker containers.
9 changes: 9 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM node:24-alpine
ARG FRONTEND_HTTP_HOST=5173
ARG FRONTEND_HMR_PORT=5174
USER node
WORKDIR /app
COPY ../package.json ../package-lock.json ./
RUN npm install --no-save
EXPOSE ${FRONTEND_HTTP_HOST}
EXPOSE ${FRONTEND_HMR_PORT}
32 changes: 32 additions & 0 deletions docker/docker-compose-atomic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: AtomicCRM

services:
frontend:
image: atomic-crm-frontend
build:
context: ..
dockerfile: ./docker/Dockerfile
command: npm run dev
environment:
NODE_ENV: development
FRONTEND_HTTP_HOST: ${FRONTEND_HTTP_HOST}
FRONTEND_HTTP_PORT: ${FRONTEND_HTTP_PORT}
FRONTEND_HMR_PORT: ${FRONTEND_HMR_PORT}
VITE_SUPABASE_URL: ${VITE_SUPABASE_URL}
VITE_SUPABASE_ANON_KEY: ${VITE_SUPABASE_ANON_KEY}
VITE_IS_DEMO: ${VITE_IS_DEMO}
VITE_INBOUND_EMAIL: ${VITE_INBOUND_EMAIL}
SKIP_PREFLIGHT_CHECK: ${SKIP_PREFLIGHT_CHECK}
ports:
- ${FRONTEND_HTTP_PORT}:${FRONTEND_HTTP_PORT}/tcp
- ${FRONTEND_HMR_PORT}:${FRONTEND_HMR_PORT}/tcp
volumes:
- ../index.html:/app/index.html
- ../demo:/app/demo
- ../src:/app/src
- ../public:/app/public
- ../vite.config.ts:/app/vite.config.ts
- ../vite.demo.config.ts:/app/vite.demo.config.ts
- ../tsconfig.json:/app/tsconfig.json
- ../tsconfig.app.json:/app/tsconfig.app.json
- ../tsconfig.node.json:/app/tsconfig.node.json
12 changes: 12 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,15 @@ registry-deploy: registry-build ## Deploy the shadcn registry (Automatically don
registry-gen: ## Generate the shadcn registry (ran automatically by a pre-commit hook)
npm run registry:gen
npx prettier --config ./.prettierrc.json --write "registry.json"

docker-init:
sh ./scripts/init-docker.sh

docker-delete:
sudo rm -rf ./docker/dev ./docker/utils ./docker/volumes

docker-start:
docker compose -p atomic-crm --env-file .env.development --env-file ./docker/.env -f ./docker/docker-compose.yml -f ./docker/docker-compose-atomic.yml up

docker-stop:
docker compose -p atomic-crm --env-file .env.development --env-file ./docker/.env -f ./docker/docker-compose.yml -f ./docker/docker-compose-atomic.yml down
8 changes: 8 additions & 0 deletions scripts/init-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'supabase-clone-temp')
git clone --depth 1 https://github.com/supabase/supabase $tmpdir
mkdir -p ./docker
cp -rf $tmpdir/docker/* ./docker
cp $tmpdir/docker/.env.example ./docker/.env
echo "You can safely answer yes to the next script step as it will only modify the .env file we just created for supabase"
cd ./docker && sh ./utils/generate-keys.sh
rm -rf $tmpdir
112 changes: 66 additions & 46 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,76 @@
import path from "node:path";
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { visualizer } from "rollup-plugin-visualizer";
import createHtmlPlugin from "vite-plugin-simple-html";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
visualizer({
open: process.env.NODE_ENV !== "CI",
filename: "./dist/stats.html",
}),
createHtmlPlugin({
minify: true,
inject: {
data: {
mainScript: `src/main.tsx`,
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
plugins: [
react(),
tailwindcss(),
visualizer({
open: process.env.NODE_ENV !== "CI",
filename: "./dist/stats.html",
}),
createHtmlPlugin({
minify: true,
inject: {
data: {
mainScript: `src/main.tsx`,
},
},
}),
],
define:
env.NODE_ENV === "production" && env.VITE_SUPABASE_URL
? {
"import.meta.env.VITE_IS_DEMO": JSON.stringify(env.VITE_IS_DEMO),
"import.meta.env.VITE_SUPABASE_URL": JSON.stringify(
env.VITE_SUPABASE_URL,
),
"import.meta.env.VITE_SUPABASE_ANON_KEY": JSON.stringify(
env.VITE_SUPABASE_ANON_KEY,
),
"import.meta.env.VITE_INBOUND_EMAIL": JSON.stringify(
env.VITE_INBOUND_EMAIL,
),
}
: undefined,
base: "./",
esbuild: {
keepNames: true,
},
build: {
sourcemap: true,
},
resolve: {
preserveSymlinks: true,
alias: {
"@": path.resolve(__dirname, "./src"),
},
}),
],
define:
process.env.NODE_ENV === "production" && process.env.VITE_SUPABASE_URL
? {
"import.meta.env.VITE_IS_DEMO": JSON.stringify(
process.env.VITE_IS_DEMO,
),
"import.meta.env.VITE_SUPABASE_URL": JSON.stringify(
process.env.VITE_SUPABASE_URL,
),
"import.meta.env.VITE_SUPABASE_ANON_KEY": JSON.stringify(
process.env.VITE_SUPABASE_ANON_KEY,
),
"import.meta.env.VITE_INBOUND_EMAIL": JSON.stringify(
process.env.VITE_INBOUND_EMAIL,
),
}
: undefined,
base: "./",
esbuild: {
keepNames: true,
},
build: {
sourcemap: true,
},
resolve: {
preserveSymlinks: true,
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
host: env.FRONTEND_HTTP_HOST === "true" ? true : env.FRONTEND_HTTP_HOST,
port: env.FRONTEND_HTTP_PORT
? parseInt(env.FRONTEND_HTTP_PORT)
: undefined,
strictPort: !!env.FRONTEND_HTTP_PORT,
origin: `http://0.0.0.0:${env.FRONTEND_HTTP_PORT}`,
hmr: {
port: env.FRONTEND_HMR_PORT
? parseInt(env.FRONTEND_HMR_PORT)
: undefined,
},
},
preview: {
host: env.FRONTEND_HTTP_HOST,
port: env.FRONTEND_HTTP_PORT
? parseInt(env.FRONTEND_HTTP_PORT)
: undefined,
strictPort: !!env.FRONTEND_HTTP_PORT,
},
};
});
Loading