-
Notifications
You must be signed in to change notification settings - Fork 1
fix: replace removed bitnami/kafka with apache/kafka, production-hard… #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # === Build artifacts === | ||
| target/ | ||
| !target/*.jar | ||
|
|
||
| # === Dashboard build deps (handled inside Docker) === | ||
| eventara-dashboard/node_modules/ | ||
| eventara-dashboard/dist/ | ||
|
|
||
| # === IDE / editor === | ||
| .idea/ | ||
| *.iml | ||
| *.iws | ||
| .vscode/ | ||
| *.swp | ||
| *.swo | ||
| *~ | ||
|
|
||
| # === OS junk === | ||
| .DS_Store | ||
| Thumbs.db | ||
|
|
||
| # === Git === | ||
| .git/ | ||
| .gitignore | ||
|
|
||
| # === Docs & non-essential === | ||
| ai-docs/ | ||
| *.md | ||
| LICENSE | ||
|
|
||
| # === Docker (don't send compose files into the build) === | ||
| docker-compose*.yaml | ||
| .env | ||
| .env.example | ||
| Dockerfile |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,37 +1,57 @@ | ||
| # Deployment (Docker image) | ||
| # Deployment | ||
|
|
||
| This guide is the fastest way to run Eventara on a single VM using Docker. | ||
| Deploy Eventara on any VPS with Docker. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Docker Engine + Docker Compose plugin installed | ||
| - A VM with ~2 CPU / 4 GB RAM is usually fine for a quick demo | ||
| - Docker Engine + Docker Compose plugin | ||
| - ~2 CPU / 4 GB RAM (minimum) | ||
|
|
||
| ## Quick deploy (recommended) | ||
|
|
||
| 1. Clone the repo and create your env file: | ||
| ## Quick deploy | ||
|
|
||
| ```bash | ||
| git clone https://github.com/tusharkhatriofficial/eventara.git | ||
| cd eventara | ||
|
|
||
| cp .env.example .env | ||
| ``` | ||
| # Edit .env — at minimum change POSTGRES_PASSWORD | ||
|
|
||
| 2. Start the stack: | ||
|
|
||
| ```bash | ||
| docker compose --env-file .env -f docker-compose.prod.yaml up -d --build | ||
| ``` | ||
|
|
||
| 3. Open: | ||
| The build takes 2-5 minutes. It compiles the React dashboard and Spring Boot app into a **single container** — both API and dashboard are served on port `8080`. | ||
|
|
||
| | What | URL | | ||
| |---|---| | ||
| | Dashboard + API | `http://<server>:8080` | | ||
| | Swagger | `http://<server>:8080/swagger-ui.html` | | ||
|
|
||
| - API: `http://<server>:8080` | ||
| - Swagger: `http://<server>:8080/swagger-ui.html` | ||
| ## Coolify | ||
|
|
||
| Note: If you have a frontend bundle in `src/main/resources/static`, Spring Boot will serve it at `/` (same `:8080`). If you want the Vite dev dashboard instead, use `docker-compose.yaml`. | ||
| 1. **Add a new resource** → Docker Compose | ||
| 2. Paste the contents of `docker-compose.prod.yaml` (or point it to the repo) | ||
| 3. Add the environment variables from `.env.example` in the **Environment Variables** tab | ||
| 4. Set the exposed port to `8080` | ||
| 5. Deploy | ||
|
|
||
| Spring profile note: the compose files don’t set `SPRING_PROFILES_ACTIVE`, so the app runs with Spring’s default profile. If you need a specific profile, add `SPRING_PROFILES_ACTIVE` to the `eventara` service in `docker-compose.prod.yaml` (or `springboot` in `docker-compose.yaml`). | ||
| Coolify will handle the build and TLS for you. | ||
|
|
||
| ## DigitalOcean / Generic Ubuntu VPS | ||
|
|
||
| ```bash | ||
| # SSH into your droplet | ||
| ssh root@<your-ip> | ||
|
|
||
| # Install Docker (if not already installed) | ||
| curl -fsSL https://get.docker.com | sh | ||
|
|
||
| # Clone and deploy | ||
| git clone https://github.com/tusharkhatriofficial/eventara.git | ||
| cd eventara | ||
| cp .env.example .env | ||
| nano .env # change POSTGRES_PASSWORD at minimum | ||
|
|
||
| docker compose --env-file .env -f docker-compose.prod.yaml up -d --build | ||
| ``` | ||
|
|
||
| ## Common operations | ||
|
|
||
|
|
@@ -46,17 +66,37 @@ docker compose -f docker-compose.prod.yaml down | |
| docker compose -f docker-compose.prod.yaml down -v | ||
| ``` | ||
|
|
||
| ## Kafka without Zookeeper (KRaft) | ||
| ## Resource limits | ||
|
|
||
| The prod compose file sets memory limits: | ||
|
|
||
| | Service | Memory Limit | | ||
| |---|---| | ||
| | Postgres | 1 GB | | ||
| | Kafka | 1 GB | | ||
| | Redis | 512 MB | | ||
| | Eventara (API + Dashboard) | 1 GB | | ||
|
|
||
| Total: ~3.5 GB. A 4 GB VPS works for demos and light use. | ||
|
|
||
|
Comment on lines
+69
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The prod docs state the compose file "sets memory limits" totaling ~3.5GB. Given This should be aligned with the actual behavior of the compose file. SuggestionClarify how limits are enforced:
Reply with "@CharlieHelps yes please" if you'd like me to add a commit updating |
||
| ## Environment variables | ||
|
|
||
| `docker-compose.yaml` and `docker-compose.prod.yaml` run Kafka in **KRaft mode**, so there is no Zookeeper container. | ||
| | Variable | Default | Description | | ||
| |---|---|---| | ||
| | `POSTGRES_USER` | `postgres` | Database user | | ||
| | `POSTGRES_PASSWORD` | `mysecretpassword` | **Change this** | | ||
| | `POSTGRES_DB` | `eventara` | Database name | | ||
| | `CLUSTER_ID` | `NvDmnaWzQgiH8qbnraqxcg` | Kafka KRaft cluster ID — don't change after first boot | | ||
| | `EVENTARA_PORT` | `8080` | Host port for the app | | ||
| | `JAVA_OPTS` | `-Xms256m -Xmx512m` | JVM memory settings | | ||
|
|
||
| Important: `KAFKA_KRAFT_CLUSTER_ID` must stay stable for a given Kafka data volume. If you change it, wipe the Kafka volume (`docker compose down -v`) before starting again. | ||
| ## Kafka (KRaft mode) | ||
|
|
||
| ## Suggested changes for a real production setup | ||
| No Zookeeper needed. `CLUSTER_ID` must stay stable for a given Kafka data volume. If you change it, wipe the volume first (`docker compose down -v`). | ||
|
|
||
| These aren’t required for a “get it up in a few hours” deployment, but they’re the usual next steps: | ||
| ## Production hardening checklist | ||
|
|
||
| 1. **Use a real image registry** (GHCR/Docker Hub) and deploy by pulling an image instead of building on the server. | ||
| 2. **Don’t publish Postgres/Redis ports** to the public internet (keep them on the Docker network only). | ||
| 3. Put Eventara behind a reverse proxy (Nginx/Caddy) for TLS and (later) auth. | ||
| 4. Replace the default credentials in `.env` and store secrets in your VM’s secret manager. | ||
| - [ ] Change default database password in `.env` | ||
| - [ ] Put behind a reverse proxy (Nginx/Caddy) for TLS | ||
| - [ ] Don't expose Postgres/Redis ports to the internet (prod compose already doesn't) | ||
| - [ ] Use a managed database if scaling beyond a single node | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,31 @@ | ||
| # ===== Stage 1: Build the app ===== | ||
| FROM maven:3.9.9-eclipse-temurin-21 AS build | ||
| # ===== Stage 1: Build the dashboard ===== | ||
| FROM node:20-alpine AS dashboard-build | ||
| WORKDIR /app/eventara-dashboard | ||
| COPY eventara-dashboard/package*.json ./ | ||
| RUN npm ci --production=false | ||
| COPY eventara-dashboard/ . | ||
| # Vite builds to ../src/main/resources/static (relative to eventara-dashboard) | ||
| RUN npm run build | ||
|
|
||
| # ===== Stage 2: Build the Spring Boot app ===== | ||
| FROM maven:3.9.9-eclipse-temurin-21 AS backend-build | ||
| WORKDIR /app | ||
| COPY pom.xml . | ||
| COPY src ./src | ||
| # Copy dashboard build output into Spring Boot static resources | ||
| COPY --from=dashboard-build /app/src/main/resources/static ./src/main/resources/static/ | ||
|
Comment on lines
+1
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Dockerfile comment says Vite builds to Given the build stage This is fragile because it relies on a non-standard output directory and a hard-coded cross-directory path. SuggestionMake the dashboard artifact path explicit and robust:
If you intentionally build directly into
Reply with "@CharlieHelps yes please" if you'd like me to add a commit that switches to copying from |
||
| RUN mvn clean package -DskipTests | ||
|
|
||
| # ===== Stage 2: Run the app ===== | ||
| FROM eclipse-temurin:21-jdk | ||
| # ===== Stage 3: Run the app ===== | ||
| FROM eclipse-temurin:21-jre | ||
| WORKDIR /app | ||
| COPY --from=build /app/target/*.jar app.jar | ||
|
|
||
| # Run as non-root | ||
| RUN groupadd -r eventara && useradd -r -g eventara eventara | ||
|
|
||
| COPY --from=backend-build /app/target/*.jar app.jar | ||
| RUN chown eventara:eventara app.jar | ||
|
|
||
| USER eventara | ||
| EXPOSE 8080 | ||
| ENTRYPOINT ["java", "-jar", "app.jar"] | ||
|
Comment on lines
30
to
31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is a functional issue: users will think memory limits are applied when they aren’t. SuggestionUpdate the entrypoint to actually apply
Or keep exec form by using
Reply with "@CharlieHelps yes please" if you'd like me to add a commit implementing this change safely (including quoting/ |
||
|
|
||
| # Simplified Dockerfile using local JAR | ||
| # FROM eclipse-temurin:21-jdk | ||
| # WORKDIR /app | ||
| # COPY target/*.jar app.jar | ||
| # EXPOSE 8080 | ||
| # ENTRYPOINT ["java", "-jar", "app.jar"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,11 @@ services: | |
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
| start_period: 30s | ||
| deploy: | ||
| resources: | ||
| limits: | ||
| memory: 1g | ||
|
|
||
| redis: | ||
| image: redis:7-alpine | ||
|
|
@@ -29,42 +34,51 @@ services: | |
| interval: 10s | ||
| timeout: 5s | ||
| retries: 5 | ||
| start_period: 10s | ||
| deploy: | ||
| resources: | ||
| limits: | ||
| memory: 512m | ||
|
|
||
| kafka: | ||
| image: bitnami/kafka:3.7 | ||
| image: apache/kafka:latest | ||
| restart: unless-stopped | ||
| environment: | ||
| ALLOW_PLAINTEXT_LISTENER: "yes" | ||
| KAFKA_KRAFT_CLUSTER_ID: ${KAFKA_KRAFT_CLUSTER_ID:-NvDmnaWzQgiH8qbnraqxcg} | ||
| CLUSTER_ID: ${CLUSTER_ID:-NvDmnaWzQgiH8qbnraqxcg} | ||
|
|
||
| KAFKA_CFG_NODE_ID: 1 | ||
| KAFKA_CFG_PROCESS_ROLES: controller,broker | ||
| KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@kafka:9094 | ||
| KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER | ||
| KAFKA_NODE_ID: 1 | ||
| KAFKA_PROCESS_ROLES: controller,broker | ||
| KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9094 | ||
| KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER | ||
|
|
||
| # Internal-only listener for production (no host port published) | ||
| KAFKA_CFG_LISTENERS: INTERNAL://:9092,CONTROLLER://:9094 | ||
| KAFKA_CFG_ADVERTISED_LISTENERS: INTERNAL://kafka:9092 | ||
| KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT | ||
| KAFKA_CFG_INTER_BROKER_LISTENER_NAME: INTERNAL | ||
| KAFKA_LISTENERS: INTERNAL://:9092,CONTROLLER://:9094 | ||
| KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:9092 | ||
| KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT | ||
| KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL | ||
|
|
||
| KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "true" | ||
| KAFKA_CFG_NUM_PARTITIONS: 3 | ||
| KAFKA_CFG_DEFAULT_REPLICATION_FACTOR: 1 | ||
| KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" | ||
| KAFKA_NUM_PARTITIONS: 3 | ||
| KAFKA_DEFAULT_REPLICATION_FACTOR: 1 | ||
|
|
||
| KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 | ||
| KAFKA_CFG_TRANSACTION_STATE_LOG_MIN_ISR: 1 | ||
| KAFKA_CFG_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 | ||
| KAFKA_CFG_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 | ||
| KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 | ||
| KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 | ||
| KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 | ||
| KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 | ||
| volumes: | ||
| - kafka_data:/bitnami/kafka | ||
| - kafka_data:/opt/kafka/kafka-logs | ||
|
Comment on lines
43
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using Given this PR’s goal is to “production-harden” deployment, the image should be pinned to a specific Kafka version (or at least a major/minor tag) and ideally to a digest. SuggestionPin the Kafka image to a known-good version and (optionally) a digest, e.g.:
Do the same in Reply with "@CharlieHelps yes please" if you'd like me to add a commit pinning the image tag in both compose files. |
||
| networks: | ||
| - eventara-network | ||
| healthcheck: | ||
| test: ["CMD", "kafka-topics.sh", "--bootstrap-server", "kafka:9092", "--list"] | ||
| test: ["CMD", "/opt/kafka/bin/kafka-topics.sh", "--bootstrap-server", "kafka:9092", "--list"] | ||
| interval: 10s | ||
| timeout: 10s | ||
| retries: 5 | ||
| start_period: 30s | ||
| deploy: | ||
| resources: | ||
| limits: | ||
| memory: 1g | ||
|
|
||
| eventara: | ||
| image: eventara:latest | ||
|
|
@@ -80,7 +94,7 @@ services: | |
| redis: | ||
| condition: service_healthy | ||
| ports: | ||
| - "8080:8080" | ||
| - "${EVENTARA_PORT:-8080}:8080" | ||
| environment: | ||
| SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-eventara} | ||
| SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-postgres} | ||
|
|
@@ -92,8 +106,15 @@ services: | |
| SPRING_DATA_REDIS_PORT: 6379 | ||
|
|
||
| EVENTARA_METRICS_DISTRIBUTED_ENABLED: "${EVENTARA_METRICS_DISTRIBUTED_ENABLED:-true}" | ||
|
|
||
| # JVM memory — adjust based on your VPS RAM | ||
| JAVA_OPTS: "${JAVA_OPTS:--Xms256m -Xmx512m}" | ||
| networks: | ||
| - eventara-network | ||
| deploy: | ||
| resources: | ||
| limits: | ||
| memory: 1g | ||
|
|
||
| volumes: | ||
| postgres_data: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You renamed
KAFKA_KRAFT_CLUSTER_ID→CLUSTER_IDin.env.exampleand compose. That’s fine for the new image, but it’s a breaking change for anyone with existing.envfiles from previous versions. The docs mention stability, but there’s no explicit migration note that users must rename the variable (or they’ll silently fall back to the default).Given the importance of cluster IDs, silently defaulting could accidentally create a new cluster metadata state when users expected their old one.
Suggestion
Add a short migration note in
DEPLOYMENT.md(or README) such as:KAFKA_KRAFT_CLUSTER_IDtoCLUSTER_IDin your.env."CLUSTER_ID: ${CLUSTER_ID:-${KAFKA_KRAFT_CLUSTER_ID:-NvDm...}}Reply with "@CharlieHelps yes please" if you'd like me to add a commit implementing backward-compatible env var fallback plus the doc note.