Skip to content

fix(deriver): disable metrics server to prevent port 9090 conflicts#468

Open
roadtogideon222-byte wants to merge 1 commit intoplastic-labs:mainfrom
roadtogideon222-byte:main
Open

fix(deriver): disable metrics server to prevent port 9090 conflicts#468
roadtogideon222-byte wants to merge 1 commit intoplastic-labs:mainfrom
roadtogideon222-byte:main

Conversation

@roadtogideon222-byte
Copy link
Copy Markdown

@roadtogideon222-byte roadtogideon222-byte commented Mar 31, 2026

Problem

The deriver container becomes unhealthy because it tries to bind port 9090 for its Prometheus metrics server (METRICS_ENABLED=true), but port 9090 is already used by the Prometheus sidecar container defined in the same docker-compose file.

Solution

  • Set METRICS_ENABLED=false for the deriver service (the API service handles metrics)
  • Added a process-based healthcheck (kill -0 1) since the deriver is a queue processor with no HTTP endpoint

Files changed

  • Added docker-compose.yml (production-ready config with example file as base)

Testing

Verified on VPS: deriver container stays healthy after restart with no port conflicts.

Summary by CodeRabbit

  • Chores
    • Added Docker Compose configuration for the complete application stack, including database, cache, API services, and monitoring tools with health checks and persistent storage.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Walkthrough

A new docker-compose.yml file is added that configures a multi-service application stack. The configuration defines six services: api and deriver built from a local Dockerfile, database using PostgreSQL with pgvector, redis for caching, and prometheus and grafana for monitoring, with explicit service dependencies and health checks.

Changes

Cohort / File(s) Summary
Docker Compose Configuration
docker-compose.yml
Adds complete multi-service stack with api/deriver services, PostgreSQL database with pgvector, Redis cache, Prometheus metrics, and Grafana dashboards. Includes service dependencies with health checks, volume mounts for persistence and virtual environments, and environment variable configuration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through services neat,
Docker containers in symphony complete,
Database, Redis, Prometheus aligned,
Grafana dashboards of metrics designed,
Compose commands bring stacks to life,
No deployment strife!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title focuses on disabling metrics to prevent port conflicts, but the main addition is a complete docker-compose.yml with six services, not just the deriver metrics fix. Consider a more comprehensive title like 'Add docker-compose.yml for multi-service application stack' or 'Add production-ready docker-compose configuration with Prometheus and Grafana' that better represents the primary change of adding the entire configuration file.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

Copy link
Copy Markdown
Contributor

@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: 3

🧹 Nitpick comments (1)
docker-compose.yml (1)

15-17: These mounts make the deploy host-stateful and can pin stale dependencies.

The image already bakes /app and /app/.venv, but these mounts override them at runtime. Docker's production docs recommend removing application-code bind mounts, and Docker volumes persist and are reused across subsequent compose up runs, so venv:/app/.venv can keep old dependencies alive after an image rebuild until someone manually removes the volume. (docs.docker.com)

📦 Suggested change
   api:
     volumes:
-      - .:/app
-      - venv:/app/.venv
@@
   deriver:
     volumes:
-      - .:/app
-      - venv:/app/.venv
@@
 volumes:
   pgdata:
-  venv:
   prometheus-data:

If you still want live-mounted code for local development, keep these mounts in a separate dev-only override file instead of the production compose file. (docs.docker.com)

Also applies to: 35-37, 115-115

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

In `@docker-compose.yml` around lines 15 - 17, The production docker-compose file
currently bind-mounts the app source (.:/app) and a persistent venv volume
(venv:/app/.venv), which overrides the image and can pin stale dependencies;
remove those mounts from docker-compose.yml (delete the "- .:/app" and "-
venv:/app/.venv" entries and any "venv" volume declaration) and instead put them
into a dev-only override file (e.g., docker-compose.override.yml or
docker-compose.dev.yml) so local live-mounting is available for development but
not for production images.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker-compose.yml`:
- Around line 29-34: The deriver service starts src.deriver directly and
bypasses docker/entrypoint.sh (which runs scripts/provision_db.py to apply
migrations), so change the deriver service's depends_on to also wait for the API
to start: add the api service under depends_on for the deriver service and use
condition: service_started (since api has no healthcheck) so the API’s
entrypoint can run migrations before src.deriver launches; reference the deriver
service, entrypoint, src.deriver, docker/entrypoint.sh, and
scripts/provision_db.py when making the change.
- Around line 18-23: The docker-compose currently hardcodes DB_CONNECTION_URI in
the api and deriver services and sets the database to use trust auth and
publishes 5432; remove the hardcoded connection string from the api and deriver
service environment blocks so they do not override .env (use env_file or
variable interpolation like DB_CONNECTION_URI=${DB_CONNECTION_URI}), remove
POSTGRES_HOST_AUTH_METHOD=trust from the database service, stop publishing port
5432 publicly (either remove ports or bind to 127.0.0.1:5432), and set explicit
secure vars for the DB in env_file (e.g., POSTGRES_USER, POSTGRES_PASSWORD,
POSTGRES_DB) so credentials come from .env and not from the compose file. Ensure
references: DB_CONNECTION_URI, env_file, database service, api service, deriver
service, and POSTGRES_HOST_AUTH_METHOD are updated accordingly.
- Around line 99-105: Replace the hardcoded Grafana environment variables
(GF_SECURITY_ADMIN_USER, GF_SECURITY_ADMIN_PASSWORD, GF_AUTH_ANONYMOUS_ENABLED,
GF_AUTH_ANONYMOUS_ORG_ROLE) with an env_file reference so secrets/config are
loaded from a separate .env or secrets file; update the docker-compose service
block that exposes port 3000 to include an env_file: entry and remove the inline
GF_SECURITY_ADMIN_USER and GF_SECURITY_ADMIN_PASSWORD lines (also consider
removing or setting GF_AUTH_ANONYMOUS_ENABLED to false in the env file) so
credentials are not committed in the compose file.

---

Nitpick comments:
In `@docker-compose.yml`:
- Around line 15-17: The production docker-compose file currently bind-mounts
the app source (.:/app) and a persistent venv volume (venv:/app/.venv), which
overrides the image and can pin stale dependencies; remove those mounts from
docker-compose.yml (delete the "- .:/app" and "- venv:/app/.venv" entries and
any "venv" volume declaration) and instead put them into a dev-only override
file (e.g., docker-compose.override.yml or docker-compose.dev.yml) so local
live-mounting is available for development but not for production images.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6dfa0895-2c0a-46ec-8f4a-c7df7be05d11

📥 Commits

Reviewing files that changed from the base of the PR and between a5423b5 and 0ca7562.

📒 Files selected for processing (1)
  • docker-compose.yml
Comment on lines +18 to +23
environment:
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
- CACHE_URL=redis://redis:6379/0?suppress=true
env_file:
- path: .env
required: false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "docker-compose.yml" -o -name "docker-compose.yaml"

Repository: plastic-labs/honcho

Length of output: 83


🏁 Script executed:

cat -n ./docker-compose.yml

Repository: plastic-labs/honcho

Length of output: 3720


🌐 Web query:

Docker Compose environment variable precedence env_file vs environment

💡 Result:

In Docker Compose, when the same environment variable is defined in both the environment attribute and env_file attribute in the compose file, the environment attribute takes precedence over env_file. The full order of precedence (highest to lowest) for environment variables in the container is: 1. Set using docker compose run --env (CLI). 2. Set with environment or env_file but with value interpolated from shell or .env file (via ${VAR}). 3. Set using just the environment attribute (plain values) in the Compose file. 4. Set using the env_file attribute in the Compose file. 5. Set in the container image via ENV in Dockerfile. Example from official docs: services: webapp: image: 'webapp' env_file: - ./webapp.env # VALUE=1.6 environment: - VALUE=1.7 Result: Container gets VALUE=1.7 from environment, overriding env_file. Note: The project-level .env file (auto-loaded) is for interpolation in the compose file, not directly for container env (unless referenced via env_file or interpolated). Multiple env_files are merged with later files overriding earlier ones.

Citations:


🌐 Web query:

Postgres POSTGRES_HOST_AUTH_METHOD=trust security implications docker official image

💡 Result:

In the official postgres Docker image, setting POSTGRES_HOST_AUTH_METHOD=trust tells the entrypoint (at initial database creation) to configure host (TCP/IP) connections to use the trust auth method—so POSTGRES_PASSWORD is not required. The image already enables trust for some local access inside the container, but this variable extends the idea to host connections. [1]

Security implications

  • No authentication for TCP clients: PostgreSQL’s trust method means anyone who can connect to the server can log in as any PostgreSQL user without a password (including potentially postgres). [2]
  • Risk becomes “network = security boundary”: If you publish Postgres to the host (-p 5432:5432), run on a shared network, misconfigure Kubernetes/Cloud networking, or otherwise make the port reachable, an attacker who can reach the port can likely get in immediately. (This is why “trust” is widely warned against outside isolated dev/test.) [2][3]
  • Hardening tools may block it: Docker’s “Hardened Images” guidance for Postgres explicitly disallows trust / password for POSTGRES_HOST_AUTH_METHOD “for security reasons” (showing how unsafe trust is considered for production-like usage). [4]

Practical guidance

  • Use POSTGRES_PASSWORD (or secrets) and a password-based method (preferably SCRAM) instead of trust, and don’t expose 5432 publicly—keep Postgres on a private Docker network or behind strict firewalling / tunneling. [1][2]

Sources: Docker Official Image docs for postgres [1], PostgreSQL docs on trust meaning “login as any user without a password” [2], Cloud Security Alliance note on enabling trust via POSTGRES_HOST_AUTH_METHOD [3], Docker Hardened Images guide noting restrictions for security [4].


Remove hardcoded database credentials and don't expose Postgres with trust authentication.

The api and deriver services hard-code DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres, and Docker Compose gives environment precedence over env_file, so any safer values in .env are ignored. The database service publishes port 5432 to all host interfaces with POSTGRES_HOST_AUTH_METHOD=trust, which allows anyone with network access to connect without a password—no credentials required. On a VPS or shared network, this is an exposed superuser database.

🛡️ Suggested changes
   api:
     environment:
-      - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
+      - DB_CONNECTION_URI=postgresql+psycopg://postgres:${POSTGRES_PASSWORD}@database:5432/postgres
       - CACHE_URL=redis://redis:6379/0?suppress=true

   deriver:
     environment:
-      - DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
+      - DB_CONNECTION_URI=postgresql+psycopg://postgres:${POSTGRES_PASSWORD}@database:5432/postgres
       - CACHE_URL=redis://redis:6379/0?suppress=true

   database:
-    ports:
-      - 5432:5432
     environment:
       - POSTGRES_DB=postgres
       - POSTGRES_USER=postgres
-      - POSTGRES_PASSWORD=postgres
-      - POSTGRES_HOST_AUTH_METHOD=trust
+      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 18 - 23, The docker-compose currently
hardcodes DB_CONNECTION_URI in the api and deriver services and sets the
database to use trust auth and publishes 5432; remove the hardcoded connection
string from the api and deriver service environment blocks so they do not
override .env (use env_file or variable interpolation like
DB_CONNECTION_URI=${DB_CONNECTION_URI}), remove POSTGRES_HOST_AUTH_METHOD=trust
from the database service, stop publishing port 5432 publicly (either remove
ports or bind to 127.0.0.1:5432), and set explicit secure vars for the DB in
env_file (e.g., POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB) so credentials
come from .env and not from the compose file. Ensure references:
DB_CONNECTION_URI, env_file, database service, api service, deriver service, and
POSTGRES_HOST_AUTH_METHOD are updated accordingly.
Comment on lines +29 to +34
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the docker-compose.yml file
find . -name "docker-compose.yml" -type f | head -5

Repository: plastic-labs/honcho

Length of output: 83


🏁 Script executed:

# Search for entrypoint.sh and provision_db.py
find . -name "entrypoint.sh" -o -name "provision_db.py" | head -20

Repository: plastic-labs/honcho

Length of output: 111


🏁 Script executed:

cat -n ./docker-compose.yml

Repository: plastic-labs/honcho

Length of output: 3720


🏁 Script executed:

cat -n ./docker/entrypoint.sh

Repository: plastic-labs/honcho

Length of output: 310


🏁 Script executed:

head -50 ./scripts/provision_db.py

Repository: plastic-labs/honcho

Length of output: 545


🏁 Script executed:

# Check if provision_db.py is essential or if init.sql covers the schema
wc -l ./scripts/provision_db.py

Repository: plastic-labs/honcho

Length of output: 91


🏁 Script executed:

# Double-check if api has any healthcheck defined further down
grep -A 20 "api:" ./docker-compose.yml | grep -i healthcheck

Repository: plastic-labs/honcho

Length of output: 45


🏁 Script executed:

# Verify what init_db() does to confirm it's schema initialization
grep -A 30 "async def init_db" ./src/db.py 2>/dev/null | head -40

Repository: plastic-labs/honcho

Length of output: 674


Wait for API startup before starting the worker.

docker/entrypoint.sh runs scripts/provision_db.py, which executes Alembic migrations and schema setup (create schema, install pgvector, run migrations). The deriver service bypasses this entirely and starts src.deriver directly. Since docker-compose only waits for services listed under depends_on, the worker can start as soon as Postgres and Redis are healthy, even before the API finishes schema initialization.

The suggested fix references api: condition: service_healthy, but the api service has no healthcheck defined, making that impossible to implement. Use condition: service_started instead to wait for the API to start, which triggers the migration sequence:

   deriver:
     depends_on:
+      api:
+        condition: service_started
       database:
         condition: service_healthy
       redis:
         condition: service_healthy
📝 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
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
api:
condition: service_started
database:
condition: service_healthy
redis:
condition: service_healthy
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 29 - 34, The deriver service starts
src.deriver directly and bypasses docker/entrypoint.sh (which runs
scripts/provision_db.py to apply migrations), so change the deriver service's
depends_on to also wait for the API to start: add the api service under
depends_on for the deriver service and use condition: service_started (since api
has no healthcheck) so the API’s entrypoint can run migrations before
src.deriver launches; reference the deriver service, entrypoint, src.deriver,
docker/entrypoint.sh, and scripts/provision_db.py when making the change.
Comment on lines +99 to +105
ports:
- 3000:3000
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the docker-compose.yml file
fd -t f "docker-compose.yml"

Repository: plastic-labs/honcho

Length of output: 89


🏁 Script executed:

# Read the docker-compose.yml.example file to see lines 99-105 in context
cat -n docker-compose.yml.example | sed -n '90,115p'

Repository: plastic-labs/honcho

Length of output: 607


🏁 Script executed:

# Read more context around the Grafana service to see if ports are defined
cat -n docker-compose.yml.example | sed -n '75,100p'

Repository: plastic-labs/honcho

Length of output: 983


🏁 Script executed:

# Get more context - check if there's a .env file or how credentials are typically managed
fd -t f "\.env" | head -5
cat -n docker-compose.yml.example | head -10

Repository: plastic-labs/honcho

Length of output: 345


🏁 Script executed:

# Check if there's a .env.example or documentation about environment variables
fd -t f "\.env" 
# Also check the README or any documentation
fd -t f "README.md"

Repository: plastic-labs/honcho

Length of output: 353


🏁 Script executed:

# Read the main README to understand the project context
head -100 README.md

Repository: plastic-labs/honcho

Length of output: 3614


🏁 Script executed:

# Check if there's any documentation in the docker directory or compose file about setup
cat -n docker-compose.yml.example | head -20
# Also check if there's a .dockerignore or setup guide
fd -t f "setup\|SETUP\|CONTRIBUTING" | head -5

Repository: plastic-labs/honcho

Length of output: 702


🏁 Script executed:

# Check the context - is this file meant to be used as-is or is there documentation about customization?
# Look for any comments or instructions in docker-compose.yml.example
head -50 docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 1328


🏁 Script executed:

# Check if there's any .gitignore or documentation about how to use this file
cat .gitignore | grep -A 5 -B 5 "docker-compose"
# Also check if there are instructions in the docker directory
ls -la docker/

Repository: plastic-labs/honcho

Length of output: 513


🏁 Script executed:

# Check the structure - this is docker-compose.yml.example (a template)
# The actual docker-compose.yml is in .gitignore (local file)
# Let's verify: does the review correctly reference the file, and what is the context for this being an example file?

# First, let's check if there's any README or documentation about the docker setup
find . -path ./node_modules -prune -o -name "*docker*" -o -name "*compose*" | grep -i readme

Repository: plastic-labs/honcho

Length of output: 45


🏁 Script executed:

# Let's verify Grafana documentation about these settings
# And check if the concern is valid for an example/template file

# First, check if there are any comments or instructions at the top of the file
head -5 docker-compose.yml.example

# Check if there's any CONTRIBUTING or setup documentation
find . -type f \( -name "CONTRIBUTING*" -o -name "DEVELOPMENT*" -o -name "*.md" \) | xargs grep -l "docker-compose" | head -3

Repository: plastic-labs/honcho

Length of output: 183


🏁 Script executed:

# Check if README or CHANGELOG mentions docker-compose setup
grep -A 5 -B 5 "docker-compose\|docker\|grafana" README.md | head -50

Repository: plastic-labs/honcho

Length of output: 1659


🏁 Script executed:

# Let me search for any documentation or comments about the security settings
# Check if Grafana service in docker-compose has env_file setup
grep -A 15 "grafana:" docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 549


🏁 Script executed:

# Check if there's a .env.template file that shows what variables should be set
fd -t f "\.env\."
# Also check if other services in the compose file use env_file
grep -B 5 -A 10 "env_file:" docker-compose.yml.example

Repository: plastic-labs/honcho

Length of output: 981


🏁 Script executed:

# Check Grafana version and verify it supports environment variable substitution
grep "grafana:" -A 2 docker-compose.yml.example
# Verify the pattern used in the file - does it support ${VAR} syntax?
grep "\${" docker-compose.yml.example | head -5

Repository: plastic-labs/honcho

Length of output: 136


🏁 Script executed:

# Check if the suggested ${VAR} syntax is used elsewhere or if env_file is the pattern
grep -E "\$\{[A-Z_]+\}" docker-compose.yml.example | head -10
# Also verify the Grafana version supports these environment variables
# Let's look at what the actual pattern is in this project for env vars
head -100 docker-compose.yml.example | grep -E "environment:|env_file:|=\$"

Repository: plastic-labs/honcho

Length of output: 158


Add env_file entry and remove hardcoded Grafana admin credentials.

Grafana is configured with default credentials (admin/admin) and anonymous access enabled on port 3000. While this is an example template, it sets a poor security baseline that users may not customize. Other services in the compose file use env_file to manage secrets—Grafana should follow the same pattern:

🔐 Suggested change
  grafana:
    image: grafana/grafana:11.4.0
    ports:
      - 3000:3000
+   env_file:
+     - path: .env
+       required: false
    environment:
-     - GF_SECURITY_ADMIN_USER=admin
-     - GF_SECURITY_ADMIN_PASSWORD=admin
-     - GF_AUTH_ANONYMOUS_ENABLED=true
+     - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER}
+     - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
+     - GF_AUTH_ANONYMOUS_ENABLED=false
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` around lines 99 - 105, Replace the hardcoded Grafana
environment variables (GF_SECURITY_ADMIN_USER, GF_SECURITY_ADMIN_PASSWORD,
GF_AUTH_ANONYMOUS_ENABLED, GF_AUTH_ANONYMOUS_ORG_ROLE) with an env_file
reference so secrets/config are loaded from a separate .env or secrets file;
update the docker-compose service block that exposes port 3000 to include an
env_file: entry and remove the inline GF_SECURITY_ADMIN_USER and
GF_SECURITY_ADMIN_PASSWORD lines (also consider removing or setting
GF_AUTH_ANONYMOUS_ENABLED to false in the env file) so credentials are not
committed in the compose file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant