Skip to content

fix: resolve local Docker setup issues#49

Merged
weroperking merged 1 commit intoweroperking:mainfrom
Helal-maker:fix/docker-local-setup-43
Mar 29, 2026
Merged

fix: resolve local Docker setup issues#49
weroperking merged 1 commit intoweroperking:mainfrom
Helal-maker:fix/docker-local-setup-43

Conversation

@Helal-maker
Copy link
Copy Markdown
Contributor

@Helal-maker Helal-maker commented Mar 29, 2026

  • Simplify docker-compose.yml for local dev with hot-reload
  • Update self-hosted compose with proper services
  • Add Dashboard Dockerfile for production
  • Update .env.example with all required variables
  • Create docker-setup.md documentation
  • Update installation.md with Docker prerequisites
  • Update server Dockerfile to Bun 1.3.9

Fixes #43

Summary by CodeRabbit

  • New Features

    • Added Inngest service to local development environment.
    • New Docker dashboard build configuration.
  • Documentation

    • Added comprehensive Docker setup guide with troubleshooting steps.
    • Updated installation guide with Docker-based setup instructions.
  • Chores

    • Updated environment configuration templates and Docker Compose files for PostgreSQL and MinIO.
    • Changed default application port from 3000 to 3001.
    • Updated server and dashboard Docker image bases.

- Simplify docker-compose.yml for local dev with hot-reload
- Update self-hosted compose with proper services
- Add Dashboard Dockerfile for production
- Update .env.example with all required variables
- Create docker-setup.md documentation
- Update installation.md with Docker prerequisites
- Update server Dockerfile to Bun 1.3.9

Fixes weroperking#43
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Walkthrough

Restructured local and production Docker infrastructure to use explicit PostgreSQL credentials, MinIO storage, and Inngest event processing. Updated environment template, compose configurations, application Dockerfiles, and installation documentation. Port changed from 3000 to 3001.

Changes

Cohort / File(s) Summary
Environment Configuration
.env.example
Switched database from DATABASE_URL template to explicit POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB, updated storage from generic S3-compatible to MinIO credentials (MINIO_ROOT_USER/MINIO_ROOT_PASSWORD, STORAGE_ENDPOINT/STORAGE_ACCESS_KEY/STORAGE_SECRET_KEY), replaced Inngest section with active INNGEST_LOG_LEVEL/INNGEST_SIGNING_KEY/INNGEST_EVENT_KEY, and changed PORT from 3000 to 3001.
Local Development Docker
docker-compose.yml
Removed app service; added inngest service (v1.17.5, port 8288); updated postgres container name/credentials (betterbase_dev_password)/volumes; updated minio container name/credentials (betterbase/betterbase_dev_password)/volumes; removed betterbase-network custom network.
Production Self-Hosted Docker
docker-compose.self-hosted.yml
Updated PostgreSQL to use .env variables with pg_isready healthcheck; switched MinIO to latest image with MINIO_ROOT_USER/MINIO_ROOT_PASSWORD; changed Inngest from latest to v1.17.5 running inngest dev, removed healthcheck; renamed server env vars (BETTERBASE_JWT_SECRETAUTH_SECRET, BETTERBASE_PUBLIC_URLAUTH_URL, CORS_ORIGINSCORS_ORIGIN); replaced dashboard build with nginx:alpine mounting ./apps/dashboard/dist.
Container Images
apps/dashboard/Dockerfile, packages/server/Dockerfile
Added new dashboard Dockerfile using Bun builder + nginx Alpine runtime with SPA routing and asset caching headers. Updated server Dockerfile from Bun 1.2-alpine to 1.3.9-alpine, added git to builder and wget to runtime, removed migrations copy.
Documentation
docs/docker-setup.md, docs/getting-started/installation.md
Added comprehensive Docker setup guide covering local development, self-hosted production, environment variables, service access credentials, and troubleshooting. Updated installation guide to Docker-focused workflow (removed bb init, added bun install + Docker services), changed base URL to http://localhost:3001, replaced database options table with Docker quick-start content.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately reflects the main objective: resolving local Docker setup issues through configuration and documentation improvements.
Linked Issues check ✅ Passed PR addresses issue #43 requirements: simplifies local Docker setup via docker-compose.yml, documents workflow in docker-setup.md, and updates installation.md with Docker prerequisites.
Out of Scope Changes check ✅ Passed All changes directly support Docker setup goals. Server Dockerfile update to Bun 1.3.9 is aligned scope (dependency maintenance for setup reliability).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Helal-maker Helal-maker mentioned this pull request Mar 29, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 93-94: Add a single trailing newline at the end of the
.env.example file so the file ends with a blank line; ensure the last lines
containing the commented variables VECTOR_PROVIDER and OPENAI_API_KEY are
followed by a newline character to satisfy EndingBlankLine static analysis.
- Around line 11-15: The DATABASE_URL in .env.example is hardcoded and will
diverge from POSTGRES_PASSWORD if users change it; update DATABASE_URL to
reference the existing variables so it derives its password (and user/db)
dynamically, e.g. replace the literal URL with a template that uses
POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB (and optionally
POSTGRES_HOST/POSTGRES_PORT) such as
postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}
so changes to POSTGRES_PASSWORD automatically apply.
- Around line 17-22: Replace the mismatched environment variable names in
.env.example to align with the server's env.ts schema: rename AUTH_SECRET to
BETTERBASE_JWT_SECRET, AUTH_URL to BETTERBASE_PUBLIC_URL, and CORS_ORIGIN to
CORS_ORIGINS; keep the same example values and comments (e.g., generation hint
for the secret) and ensure CORS_ORIGINS uses the expected format
(comma-separated or array as env.ts expects) so the server reads the variables
correctly.

In `@apps/dashboard/Dockerfile`:
- Around line 13-28: The Dockerfile is copying root files and building from repo
root; change it to build specifically for the dashboard by setting WORKDIR to
the dashboard folder, copy only the dashboard package files, run bun install and
build in that context, and copy the dashboard dist to nginx. Concretely: set
WORKDIR /app/apps/dashboard (or copy apps/dashboard into /app), replace COPY
package.json bun.lock* ./ with COPY apps/dashboard/package.json
apps/dashboard/bun.lock* ./ (or COPY apps/dashboard . .), run RUN bun install
--frozen-lockfile in that dashboard workdir, run RUN bun run build (which will
now run the dashboard build script), and update the final stage COPY
--from=builder to copy /app/apps/dashboard/dist (or /app/dist if you build into
/app) to /usr/share/nginx/html so the built dashboard assets are used.
- Around line 26-53: Add a non-root runtime user and a container HEALTHCHECK:
create a limited user and group, chown /usr/share/nginx/html and any needed
runtime dirs, switch to that user before the CMD (reference the existing RUN ...
/etc/nginx/conf.d/default.conf block and CMD ["nginx", "-g", "daemon off;"] and
EXPOSE 80), and add a HEALTHCHECK instruction that probes the nginx HTTP
endpoint (e.g., curl -f http://localhost:80/health or /) to return success; also
ensure the nginx config (the server block written by RUN cat >
/etc/nginx/conf.d/default.conf) includes a simple /health location that returns
200 so the healthcheck works.

In `@docker-compose.self-hosted.yml`:
- Line 40: Replace the unpinned MinIO images "minio/minio:latest" and
"minio/mc:latest" with specific release tags (e.g.,
"minio/minio:RELEASE.2024-<x>" and "minio/mc:RELEASE.2024-<x>") to avoid
unexpected breaking changes; update the two occurrences of image:
minio/minio:latest and image: minio/mc:latest in the compose file to use pinned
RELEASE.* tags (or configurable env vars) so production deployments use a fixed,
tested MinIO release.
- Around line 121-130: The docker-compose dashboard service mounts pre-built
frontend assets at ./apps/dashboard/dist:/usr/share/nginx/html:ro (service name
"dashboard"), but the README only mentions running "bun run build" for the
server; update documentation or add an explicit build step: instruct users to
run "bun --cwd apps/dashboard run build" (or equivalent) to produce
./apps/dashboard/dist before starting docker-compose, or add a compose build
step (e.g., a build stage or an auxiliary service) that runs the dashboard build
automatically so the volume has the required files.
- Around line 99-112: Update the environment variable names and value
constraints in the docker-compose service to match the server's expectations:
replace AUTH_SECRET with BETTERBASE_JWT_SECRET (ensure this secret is at least
32 chars to satisfy the env.ts Zod schema), replace AUTH_URL with
BETTERBASE_PUBLIC_URL, and replace CORS_ORIGIN with CORS_ORIGINS (plural);
verify these map to the same values currently provided (e.g., keep existing
defaults like http://localhost or the MinIO settings) so auth.ts (which reads
BETTERBASE_JWT_SECRET), routes/device/index.ts (which reads
BETTERBASE_PUBLIC_URL) and index.ts (which reads CORS_ORIGINS) pass Zod
validation and runtime checks.

In `@docker-compose.yml`:
- Around line 38-40: Docker Compose currently hardcodes MINIO_ROOT_USER and
MINIO_ROOT_PASSWORD values that don't match the .env.example defaults; update
the setup so credentials are consistent by either (A) replacing the hardcoded
MINIO_ROOT_USER and MINIO_ROOT_PASSWORD in docker-compose.yml with environment
variable interpolation that reads from .env (e.g., ${MINIO_ROOT_USER} and
${MINIO_ROOT_PASSWORD}), or (B) update .env.example to list the exact values
used in docker-compose.yml (betterbase / betterbase_dev_password). Ensure the
tokens referenced are MINIO_ROOT_USER and MINIO_ROOT_PASSWORD and confirm
.env.example lines for those keys match the docker-compose values.
- Around line 32-51: Add a one-off minio-init service to the compose that waits
for the minio service to be healthy and creates the betterbase bucket
automatically; reference the existing minio service name (minio) in depends_on
and the healthcheck, make the init container use a lightweight image (awscli or
minio/mc), run a startup script that polls http://minio:9000/minio/health/live
until healthy then run the bucket creation command (e.g., aws s3api
create-bucket or mc mb) with credentials from MINIO_ROOT_USER /
MINIO_ROOT_PASSWORD, and set the service to not restart (or exit after success)
so it only runs once during local compose startup.

In `@docs/docker-setup.md`:
- Line 125: Update the fenced code block opener in docs/docker-setup.md to
include the bash language specifier by replacing the plain backticks ("```")
with "```bash" so the shell snippet is highlighted correctly; locate the
existing code fence around the "Start services" block and change its opening
fence to "```bash".
- Around line 67-93: Update the MinIO credentials in the docs to match the
actual compose files: change the "MinIO Console" credentials in the table and
the troubleshooting commands to reflect the local-dev credentials from
docker-compose.yml (betterbase/betterbase_dev_password) and the self-hosted
defaults from docker-compose.self-hosted.yml (minioadmin/minioadmin); explicitly
state which credential set applies to which compose file and update the example
mc alias/mb commands used by the minio-init/minio services so they use the
correct username/password pair for the selected environment.

In `@docs/getting-started/installation.md`:
- Around line 125-137: The fenced code block showing the project directory tree
(the block beginning with the triple backticks and the "betterbase/" tree) is
missing a language specifier which triggers MD040; update the opening fence from
``` to ```text (or another consistent neutral specifier) so the block becomes a
text-qualified fenced code block to satisfy static analysis and keep formatting
consistent.

In `@packages/server/Dockerfile`:
- Line 27: The runtime image is missing the migrations directory so migrate.ts
(MIGRATIONS_DIR) and runMigrations(pool) will fail when they attempt to
readdir/readFile SQL files; update the Dockerfile to copy the built migrations
into the runtime image (add a COPY --from=builder step to copy
/app/packages/server/migrations to the container path expected at runtime,
ensuring the files land where join(__dirname, "../../migrations") resolves
relative to dist) so the migration .sql files are available at startup.
- Around line 21-25: Update the APK wget install to pin the correct Alpine 3.22
package version by changing the RUN line that currently uses "apk add --no-cache
wget" to explicitly install "wget=1.25.0-r1"; do not add or modify any COPY of
migrations (remove the suggested migrations COPY from the proposed diff) and
retain the non-root hardening changes as-is since the server only exposes port
3001.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 100b7beb-50bb-48a0-a629-576762809b93

📥 Commits

Reviewing files that changed from the base of the PR and between f746186 and b056d82.

📒 Files selected for processing (7)
  • .env.example
  • apps/dashboard/Dockerfile
  • docker-compose.self-hosted.yml
  • docker-compose.yml
  • docs/docker-setup.md
  • docs/getting-started/installation.md
  • packages/server/Dockerfile

Comment on lines +11 to +15
POSTGRES_USER=betterbase
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_DB=betterbase

# Or for Neon (serverless PostgreSQL)
# DATABASE_URL=postgres://user:password@ep-xxx.us-east-1.aws.neon.tech/neondb?sslmode=require

# Or for Turso (libSQL)
# TURSO_URL=libsql://your-database.turso.io
# TURSO_AUTH_TOKEN=your-auth-token
DATABASE_URL=postgresql://betterbase:your_secure_password_here@localhost:5432/betterbase
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

DATABASE_URL password is hardcoded, not derived from POSTGRES_PASSWORD.

POSTGRES_PASSWORD is set to your_secure_password_here but DATABASE_URL has the same literal string hardcoded. Users who change POSTGRES_PASSWORD may forget to update DATABASE_URL, causing connection failures.

Suggested improvement
 POSTGRES_USER=betterbase
 POSTGRES_PASSWORD=your_secure_password_here
 POSTGRES_DB=betterbase
 
-DATABASE_URL=postgresql://betterbase:your_secure_password_here@localhost:5432/betterbase
+# Construct from above values: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}
+DATABASE_URL=postgresql://betterbase:your_secure_password_here@localhost:5432/betterbase
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 12-12: [UnorderedKey] The POSTGRES_PASSWORD key should go before the POSTGRES_USER key

(UnorderedKey)


[warning] 13-13: [UnorderedKey] The POSTGRES_DB key should go before the POSTGRES_PASSWORD key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 11 - 15, The DATABASE_URL in .env.example is
hardcoded and will diverge from POSTGRES_PASSWORD if users change it; update
DATABASE_URL to reference the existing variables so it derives its password (and
user/db) dynamically, e.g. replace the literal URL with a template that uses
POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB (and optionally
POSTGRES_HOST/POSTGRES_PORT) such as
postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}
so changes to POSTGRES_PASSWORD automatically apply.

Comment on lines 17 to 22
# ----------------------------------------------------------------------------
# Required: Authentication
# Authentication
# ----------------------------------------------------------------------------
# Generate a secure secret: openssl rand -base64 32
AUTH_SECRET=your-super-secret-key-min-32-chars-long-change-in-production
# Generate: openssl rand -base64 32
AUTH_SECRET=your-super-secret-key-min-32-chars-long
AUTH_URL=http://localhost:3000
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Environment variable names don't match server code.

Lines 21-22 define AUTH_SECRET and AUTH_URL, but the server's env.ts schema expects BETTERBASE_JWT_SECRET and BETTERBASE_PUBLIC_URL. Same issue with CORS_ORIGIN (line 51) vs expected CORS_ORIGINS.

Either update the server code or use the expected variable names here.

Proposed fix to match server expectations
 # ----------------------------------------------------------------------------
 # Authentication
 # ----------------------------------------------------------------------------
 # Generate: openssl rand -base64 32
-AUTH_SECRET=your-super-secret-key-min-32-chars-long
-AUTH_URL=http://localhost:3000
+BETTERBASE_JWT_SECRET=your-super-secret-key-min-32-chars-long
+BETTERBASE_PUBLIC_URL=http://localhost:3000
-CORS_ORIGIN=http://localhost:3000,http://localhost:5173
+CORS_ORIGINS=http://localhost:3000,http://localhost:5173
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 17 - 22, Replace the mismatched environment
variable names in .env.example to align with the server's env.ts schema: rename
AUTH_SECRET to BETTERBASE_JWT_SECRET, AUTH_URL to BETTERBASE_PUBLIC_URL, and
CORS_ORIGIN to CORS_ORIGINS; keep the same example values and comments (e.g.,
generation hint for the secret) and ensure CORS_ORIGINS uses the expected format
(comma-separated or array as env.ts expects) so the server reads the variables
correctly.

Comment on lines +93 to +94
# VECTOR_PROVIDER=openai
# OPENAI_API_KEY=your-openai-api-key No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Missing trailing newline.

Static analysis flags missing blank line at end of file (EndingBlankLine).

Fix
 # OPENAI_API_KEY=your-openai-api-key
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# VECTOR_PROVIDER=openai
# OPENAI_API_KEY=your-openai-api-key
# VECTOR_PROVIDER=openai
# OPENAI_API_KEY=your-openai-api-key
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 94-94: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 93 - 94, Add a single trailing newline at the end
of the .env.example file so the file ends with a blank line; ensure the last
lines containing the commented variables VECTOR_PROVIDER and OPENAI_API_KEY are
followed by a newline character to satisfy EndingBlankLine static analysis.

Comment on lines +13 to +28
WORKDIR /app

COPY package.json bun.lock* ./

RUN bun install --frozen-lockfile

COPY . .

ARG VITE_API_URL=http://localhost:3001
ENV VITE_API_URL=$VITE_API_URL

RUN bun run build

FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Build context and paths are incorrect for monorepo structure.

The build instructions specify building from repo root (docker build -f apps/dashboard/Dockerfile -t betterbase-dashboard .), so:

  1. Line 15 copies root package.json, not apps/dashboard/package.json
  2. Line 17 bun install --frozen-lockfile installs root deps, may fail if bun.lock is optional (bun.lock* glob)
  3. Line 19 COPY . . copies entire monorepo
  4. Line 24 bun run build runs root's build script, not dashboard's
  5. Line 28 expects dist at /app/dist but dashboard build outputs to /app/apps/dashboard/dist
Proposed fix for monorepo-aware build
 FROM oven/bun:1.3.9-debian AS builder
 
 WORKDIR /app
 
-COPY package.json bun.lock* ./
+COPY package.json bun.lock ./
+COPY apps/dashboard/package.json ./apps/dashboard/
 
 RUN bun install --frozen-lockfile
 
 COPY . .
 
+WORKDIR /app/apps/dashboard
+
 ARG VITE_API_URL=http://localhost:3001
 ENV VITE_API_URL=$VITE_API_URL
 
-RUN bun run build
+RUN cd /app/apps/dashboard && bun run build
 
 FROM nginx:alpine
 
-COPY --from=builder /app/dist /usr/share/nginx/html
+COPY --from=builder /app/apps/dashboard/dist /usr/share/nginx/html
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile
COPY . .
ARG VITE_API_URL=http://localhost:3001
ENV VITE_API_URL=$VITE_API_URL
RUN bun run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
WORKDIR /app
COPY package.json bun.lock ./
COPY apps/dashboard/package.json ./apps/dashboard/
RUN bun install --frozen-lockfile
COPY . .
WORKDIR /app/apps/dashboard
ARG VITE_API_URL=http://localhost:3001
ENV VITE_API_URL=$VITE_API_URL
RUN cd /app/apps/dashboard && bun run build
FROM nginx:alpine
COPY --from=builder /app/apps/dashboard/dist /usr/share/nginx/html
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/Dockerfile` around lines 13 - 28, The Dockerfile is copying
root files and building from repo root; change it to build specifically for the
dashboard by setting WORKDIR to the dashboard folder, copy only the dashboard
package files, run bun install and build in that context, and copy the dashboard
dist to nginx. Concretely: set WORKDIR /app/apps/dashboard (or copy
apps/dashboard into /app), replace COPY package.json bun.lock* ./ with COPY
apps/dashboard/package.json apps/dashboard/bun.lock* ./ (or COPY apps/dashboard
. .), run RUN bun install --frozen-lockfile in that dashboard workdir, run RUN
bun run build (which will now run the dashboard build script), and update the
final stage COPY --from=builder to copy /app/apps/dashboard/dist (or /app/dist
if you build into /app) to /usr/share/nginx/html so the built dashboard assets
are used.

Comment on lines +26 to +53
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html

RUN cat > /etc/nginx/conf.d/default.conf << 'EOF'
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;

location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}

location / {
try_files $uri $uri/ /index.html;
}

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
EOF

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"] No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add healthcheck and non-root user for production hardening.

The nginx runtime stage lacks a healthcheck and runs as root. For production self-hosted deployments, these are recommended.

Proposed additions
 FROM nginx:alpine
 
 COPY --from=builder /app/apps/dashboard/dist /usr/share/nginx/html
 
 RUN cat > /etc/nginx/conf.d/default.conf << 'EOF'
 ...
 EOF
 
+RUN chown -R nginx:nginx /usr/share/nginx/html
+USER nginx
+
+HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
+  CMD wget -qO- http://localhost:80/ || exit 1
+
 EXPOSE 80
 
 CMD ["nginx", "-g", "daemon off;"]
🧰 Tools
🪛 Checkov (3.2.510)

[low] 1-53: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-53: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)

[error] 31-31: unexpected 's'
expecting '#', '', ADD, ARG, CMD, COPY, ENTRYPOINT, ENV, EXPOSE, FROM, HEALTHCHECK, LABEL, MAINTAINER, ONBUILD, RUN, SHELL, STOPSIGNAL, USER, VOLUME, WORKDIR, a pragma, at least one space, or end of input

(DL1000)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/Dockerfile` around lines 26 - 53, Add a non-root runtime user
and a container HEALTHCHECK: create a limited user and group, chown
/usr/share/nginx/html and any needed runtime dirs, switch to that user before
the CMD (reference the existing RUN ... /etc/nginx/conf.d/default.conf block and
CMD ["nginx", "-g", "daemon off;"] and EXPOSE 80), and add a HEALTHCHECK
instruction that probes the nginx HTTP endpoint (e.g., curl -f
http://localhost:80/health or /) to return success; also ensure the nginx config
(the server block written by RUN cat > /etc/nginx/conf.d/default.conf) includes
a simple /health location that returns 200 so the healthcheck works.

Comment on lines +67 to +93
| Service | URL | Credentials |
|---------|-----|--------------|
| Betterbase API | http://localhost | Through nginx |
| Dashboard | http://localhost | Admin login |
| MinIO Console | http://localhost:9001 | minioadmin/minioadmin_password |

## Common Issues

### PostgreSQL Connection Failed

```bash
# Check if PostgreSQL is running
docker compose ps postgres

# View logs
docker compose logs postgres
```

### MinIO Bucket Not Found

The `minio-init` service creates the bucket automatically. If it fails:

```bash
# Manually create bucket
docker compose exec minio mc alias set local http://localhost:9000 minioadmin minioadmin_password
docker compose exec minio mc mb local/betterbase
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

MinIO credentials in documentation don't match compose files.

Line 71 and 91-92 document MinIO credentials as minioadmin/minioadmin_password, but:

  • docker-compose.yml (local dev) uses betterbase/betterbase_dev_password
  • docker-compose.self-hosted.yml defaults to minioadmin/minioadmin

Users following the troubleshooting steps will fail to authenticate.

Proposed fix for local development context
 | Service | URL | Credentials |
 |---------|-----|--------------|
 | Betterbase API | http://localhost | Through nginx |
 | Dashboard | http://localhost | Admin login |
-| MinIO Console | http://localhost:9001 | minioadmin/minioadmin_password |
+| MinIO Console | http://localhost:9001 | betterbase/betterbase_dev_password (local) or check your .env |
 # Manually create bucket
-docker compose exec minio mc alias set local http://localhost:9000 minioadmin minioadmin_password
-docker compose exec minio mc mb local/betterbase
+docker compose exec minio mc alias set local http://localhost:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
+docker compose exec minio mc mb local/betterbase
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docker-setup.md` around lines 67 - 93, Update the MinIO credentials in
the docs to match the actual compose files: change the "MinIO Console"
credentials in the table and the troubleshooting commands to reflect the
local-dev credentials from docker-compose.yml
(betterbase/betterbase_dev_password) and the self-hosted defaults from
docker-compose.self-hosted.yml (minioadmin/minioadmin); explicitly state which
credential set applies to which compose file and update the example mc alias/mb
commands used by the minio-init/minio services so they use the correct
username/password pair for the selected environment.


## Docker Commands Reference

```bash
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add language specifier to fenced code block.

Proposed fix
-```
+```bash
 # Start services
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 125-125: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/docker-setup.md` at line 125, Update the fenced code block opener in
docs/docker-setup.md to include the bash language specifier by replacing the
plain backticks ("```") with "```bash" so the shell snippet is highlighted
correctly; locate the existing code fence around the "Start services" block and
change its opening fence to "```bash".

Comment on lines 125 to 137
```
my-app/
├── betterbase.config.ts # Project configuration
├── drizzle.config.ts # Database configuration
├── src/
│ ├── db/
├── schema.ts # Database schema
└── migrate.ts # Migration utilities
├── functions/ # Serverless functions
── auth/ # Authentication setup
│ └── routes/ # API routes
└── package.json
betterbase/
├── packages/
├── core/ # Core framework
├── cli/ # CLI tools
│ ├── client/ # Client SDK
│ ├── server/ # Server implementation
│ └── shared/ # Shared utilities
├── apps/
── dashboard/ # Admin dashboard
── docker-compose.yml # Local development services
└── docs/ # Documentation
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add language specifier to fenced code block.

Static analysis (MD040) flags the code block at line 125 as missing a language. Since this shows directory structure, use text or leave empty but be consistent.

Proposed fix
-```
+```text
 betterbase/
 ├── packages/
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 125-125: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/getting-started/installation.md` around lines 125 - 137, The fenced code
block showing the project directory tree (the block beginning with the triple
backticks and the "betterbase/" tree) is missing a language specifier which
triggers MD040; update the opening fence from ``` to ```text (or another
consistent neutral specifier) so the block becomes a text-qualified fenced code
block to satisfy static analysis and keep formatting consistent.

Comment on lines +21 to +25
FROM oven/bun:1.3.9-alpine

WORKDIR /app

RUN apk add --no-cache wget
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🌐 Web query:

What is the latest wget version available in Alpine 3.21?

💡 Result:

The latest wget version available in Alpine 3.21 is 1.25.0-r0, from the main repository.

Citations:


🏁 Script executed:

# Check if there's a Dockerfile in the repository to see full context
fd -t f "Dockerfile" packages/server/

Repository: weroperking/Betterbase

Length of output: 92


🌐 Web query:

What Alpine version is used in oven/bun:1.3.9-alpine?

💡 Result:

The oven/bun:1.3.9-alpine Docker image uses Alpine Linux version 3.22.

Citations:


🌐 Web query:

What wget versions are available in Alpine 3.22?

💡 Result:

In Alpine Linux 3.22, the available wget version is 1.25.0-r1 from the main repository. There is no wget package in the community repository. Note that wget2 (version 2.2.0) is available in the community repository of later branches like 3.23 or edge, but not in 3.22 main or community.

Citations:


🏁 Script executed:

cat -n packages/server/Dockerfile

Repository: weroperking/Betterbase

Length of output: 1130


Update proposed wget version to 1.25.0-r1 for Alpine 3.22; remove migrations COPY from suggestion.

The non-root user hardening is valid — the server exposes port 3001 (no elevated privileges needed). However, the proposed wget=1.24.5-r0 is outdated; Alpine 3.22 (used by oven/bun:1.3.9-alpine) ships 1.25.0-r1. Also, the diff suggests copying migrations, which is not in the original Dockerfile — remove that unless required by your build output.

Corrected hardening
 FROM oven/bun:1.3.9-alpine
 
 WORKDIR /app
 
-RUN apk add --no-cache wget
+RUN apk add --no-cache wget=1.25.0-r1 \
+    && addgroup -S betterbase && adduser -S betterbase -G betterbase
 
 COPY --from=builder /app/packages/server/dist ./dist
+
+USER betterbase
🧰 Tools
🪛 Hadolint (2.14.0)

[warning] 25-25: Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>

(DL3018)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/Dockerfile` around lines 21 - 25, Update the APK wget install
to pin the correct Alpine 3.22 package version by changing the RUN line that
currently uses "apk add --no-cache wget" to explicitly install "wget=1.25.0-r1";
do not add or modify any COPY of migrations (remove the suggested migrations
COPY from the proposed diff) and retain the non-root hardening changes as-is
since the server only exposes port 3001.


RUN apk add --no-cache wget

COPY --from=builder /app/packages/server/dist ./dist
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Migrations directory not copied to runtime image.

The packages/server/migrations directory is no longer copied into the runtime image, but migrate.ts reads .sql files from disk at runtime via readdir() and readFile() on MIGRATIONS_DIR = join(__dirname, "../../migrations"). The bun build command bundles JavaScript but does not bundle external SQL files.

The server will crash on startup when runMigrations(pool) attempts to read non-existent migration files.

Proposed fix
 COPY --from=builder /app/packages/server/dist ./dist
+COPY --from=builder /app/packages/server/migrations ./migrations
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/Dockerfile` at line 27, The runtime image is missing the
migrations directory so migrate.ts (MIGRATIONS_DIR) and runMigrations(pool) will
fail when they attempt to readdir/readFile SQL files; update the Dockerfile to
copy the built migrations into the runtime image (add a COPY --from=builder step
to copy /app/packages/server/migrations to the container path expected at
runtime, ensuring the files land where join(__dirname, "../../migrations")
resolves relative to dist) so the migration .sql files are available at startup.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

❌ Failed to clone repository into sandbox. Please try again.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

❌ Failed to clone repository into sandbox. Please try again.

@weroperking weroperking merged commit 8da3f63 into weroperking:main Mar 29, 2026
4 checks passed
This was referenced Mar 29, 2026
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: local setup

2 participants