Skip to content

Conversation

@pratapalakshmi
Copy link
Contributor

@pratapalakshmi pratapalakshmi commented Dec 15, 2025

  • Updated Dockerfiles to use Caddy as the web server instead of nginx.
  • Added Caddyfile configurations for both admin and web services.
  • Implemented rate limiting in Caddy using xcaddy.
  • Adjusted healthcheck endpoint to reflect new routing in Caddy.

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

References

Summary by CodeRabbit

  • Chores
    • Replaced web server across admin and web apps with Caddy-based deployment
    • Added built-in rate-limiting and improved logging to stdout
    • Preserved service port (3000) and existing health checks
    • Enhanced static file serving and SPA-friendly routing for client apps

✏️ Tip: You can customize this high-level summary in your review settings.

- Updated Dockerfiles to use Caddy as the web server instead of nginx.
- Added Caddyfile configurations for both admin and web services.
- Implemented rate limiting in Caddy using xcaddy.
- Adjusted healthcheck endpoint to reflect new routing in Caddy.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Walkthrough

Replaces Nginx with Caddy for both admin and web apps: Dockerfiles now build a custom Caddy (xcaddy + ratelimit) and use Caddy in production; nginx.conf files were removed and new Caddyfile configs serve SPA static files on port 3000.

Changes

Cohort / File(s) Summary
Dockerfiles (admin & web)
apps/admin/Dockerfile.admin, apps/web/Dockerfile.web
Multi-stage Docker builds replaced Nginx deployment stage with an xcaddy builder stage and a Caddy production stage. Removes nginx references, copies built client into Caddy document root, updates healthcheck and CMD to run Caddy.
Caddy configurations
apps/admin/caddy/Caddyfile, apps/web/caddy/Caddyfile
New Caddyfile(s) added: bind on port 3000, set trusted_proxies, serve static files from /usr/share/caddy/html with SPA-friendly fallback (try_files {path} /index.html), enable file_server, and log access to stdout.
Removed Nginx configs
apps/admin/nginx/nginx.conf, apps/web/nginx/nginx.conf
Deleted existing Nginx configuration files and related server blocks and rate-limit/real-ip directives.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • Check xcaddy build invocation and that the ratelimit module is included and compatible with the target Caddy version.
  • Verify the built Caddy binary is copied correctly between stages and the runtime image has necessary permissions.
  • Confirm Caddyfile SPA fallback (try_files {path} /index.html) matches the served file layout and index presence.
  • Validate healthcheck URL, exposed port 3000, and CMD produce a healthy container start.
  • Review removal of Nginx rate-limit/real-ip config to ensure equivalent behavior is implemented or intentionally omitted.

Poem

🐰 I hopped from nginx to Caddy's land,
Built with xcaddy by careful hand,
Static roots and tiny logs,
Rate limits watch like garden dogs,
A sprightly deploy—carrot in hand. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description provides initial context for the changes but lacks completion of required template sections including type of change selection, test scenarios, and verification details. Select appropriate change type(s), document test scenarios that verify the Caddy migration works correctly, and include any references to related issues or PRs.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating from nginx to Caddy for admin and web services, which is clearly reflected in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2edc11f and e673161.

📒 Files selected for processing (3)
  • apps/admin/nginx/nginx.conf (0 hunks)
  • apps/web/Dockerfile.web (1 hunks)
  • apps/web/nginx/nginx.conf (0 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/nginx/nginx.conf
  • apps/admin/nginx/nginx.conf
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/Dockerfile.web
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)

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
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)
apps/admin/Dockerfile.admin (1)

76-98: Caddy multi‑stage looks good; double‑check curl availability for HEALTHCHECK

The Caddy build + production stages and static file layout look sound, and copying /usr/bin/caddy from caddy-builder correctly brings in the ratelimit‑enabled binary.

One thing to verify: HEALTHCHECK uses curl, but the caddy:2.11-alpine base typically doesn’t ship with curl by default. If you rely on Docker’s health status, consider either:

  • Installing curl in the production stage (e.g., RUN apk add --no-cache curl), or
  • Switching the healthcheck to a tool that’s already present in the image.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22339b9 and 2edc11f.

📒 Files selected for processing (4)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/caddy/Caddyfile (1 hunks)
  • apps/web/Dockerfile.web (1 hunks)
  • apps/web/caddy/Caddyfile (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)

Comment on lines +1 to +17
{
servers {
trusted_proxies static 0.0.0.0
}
}

:3000 {
root * /usr/share/caddy/html
try_files {path} /index.html
file_server

# Access logs to stdout (default in Caddy)
log {
output stdout
format console
}
} No newline at end of file
Copy link
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:

# Find relevant Dockerfile and Caddyfile configurations
find . -type f -name "Dockerfile*" | grep -E "(admin|web)" | head -20

Repository: makeplane/plane

Length of output: 174


🏁 Script executed:

# Check the admin build copy destination
find . -path "*/admin/*" -name "Dockerfile*" -exec cat {} \;

Repository: makeplane/plane

Length of output: 174


🏁 Script executed:

# Look at the web Caddyfile for comparison
find . -type f -name "Caddyfile" | head -10

Repository: makeplane/plane

Length of output: 114


🏁 Script executed:

cat -n ./apps/admin/caddy/Caddyfile

Repository: makeplane/plane

Length of output: 400


🏁 Script executed:

cat -n ./apps/web/caddy/Caddyfile

Repository: makeplane/plane

Length of output: 400


🏁 Script executed:

# Find admin Dockerfile and examine the COPY instructions
find ./apps/admin -name "Dockerfile*" -exec cat -n {} +

Repository: makeplane/plane

Length of output: 4143


Admin SPA fallback for /god-mode is broken — requests to nested routes will fail

With root * /usr/share/caddy/html and the admin build copied to /usr/share/caddy/html/god-mode, requests like /god-mode/projects/123 will attempt to fall back to /usr/share/caddy/html/index.html instead of /usr/share/caddy/html/god-mode/index.html. Since the latter directory exists but the former does not in this container, deep-linked admin routes will fail. Fix this by updating the fallback path:

-	try_files {path} /index.html
+	try_files {path} /god-mode/index.html

Additional issues:

  • trusted_proxies static 0.0.0.0 effectively trusts all IPv4 client addresses. Constrain this to your actual proxy/load-balancer IP ranges or remove it if not needed.
  • The ratelimit plugin is compiled into Caddy (via xcaddy) but no rate_limit directive is configured in the Caddyfile, leaving it unused.
📝 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
{
servers {
trusted_proxies static 0.0.0.0
}
}
:3000 {
root * /usr/share/caddy/html
try_files {path} /index.html
file_server
# Access logs to stdout (default in Caddy)
log {
output stdout
format console
}
}
{
servers {
trusted_proxies static 0.0.0.0
}
}
:3000 {
root * /usr/share/caddy/html
try_files {path} /god-mode/index.html
file_server
# Access logs to stdout (default in Caddy)
log {
output stdout
format console
}
}
🤖 Prompt for AI Agents
In apps/admin/caddy/Caddyfile around lines 1 to 17, the SPA fallback currently
always serves /usr/share/caddy/html/index.html which breaks deep links under
/god-mode (e.g. /god-mode/projects/123) because the admin build lives in
/usr/share/caddy/html/god-mode; update the fallback so requests under the
/god-mode path return /usr/share/caddy/html/god-mode/index.html (use a
conditional or a separate site block/route that sets try_files {path}
/god-mode/index.html for paths starting with /god-mode). Also tighten or remove
the overly broad trusted_proxies static 0.0.0.0 entry to only include your
proxy/load-balancer CIDRs (or delete it if not needed) and either configure the
rate_limit directive where required or remove the unused rate-limit plugin from
the build to avoid confusion.

Comment on lines +1 to +17
{
servers {
trusted_proxies static 0.0.0.0
}
}

:3000 {
root * /usr/share/caddy/html
try_files {path} /index.html
file_server

# Access logs to stdout (default in Caddy)
log {
output stdout
format console
}
} No newline at end of file
Copy link
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, check if the Caddyfile exists and view its content
find . -name "Caddyfile" -type f

Repository: makeplane/plane

Length of output: 114


🏁 Script executed:

# Search for Docker build configurations that might contain xcaddy with ratelimit
find . -name "Dockerfile*" -type f | head -20

Repository: makeplane/plane

Length of output: 399


🏁 Script executed:

# Look for any rate_limit directives in the codebase
rg "rate_limit" --type-list

Repository: makeplane/plane

Length of output: 448


🏁 Script executed:

# Correct search for rate_limit directives
rg "rate_limit" --type caddy

Repository: makeplane/plane

Length of output: 88


🏁 Script executed:

# View the Caddyfile being reviewed
cat -n ./apps/web/caddy/Caddyfile

Repository: makeplane/plane

Length of output: 400


🏁 Script executed:

# Check the Docker build config for the web app
cat -n ./apps/web/Dockerfile.web | head -50

Repository: makeplane/plane

Length of output: 2194


🏁 Script executed:

# Look for xcaddy or ratelimit references in Docker files
rg "xcaddy|ratelimit|caddy-ratelimit" Dockerfile

Repository: makeplane/plane

Length of output: 109


🏁 Script executed:

# Search for xcaddy or ratelimit in all Dockerfiles
rg "xcaddy|ratelimit|caddy-ratelimit" ./apps/web/

Repository: makeplane/plane

Length of output: 177


🏁 Script executed:

# Check the full web Dockerfile.web
cat -n ./apps/web/Dockerfile.web | tail -50

Repository: makeplane/plane

Length of output: 1817


🏁 Script executed:

# Search the admin Caddyfile for comparison
cat -n ./apps/admin/caddy/Caddyfile

Repository: makeplane/plane

Length of output: 400


🏁 Script executed:

# Search for any rate_limit in all files
rg "rate_limit"

Repository: makeplane/plane

Length of output: 998


🏁 Script executed:

# Check Caddy documentation or configuration context - look for comments or related config
rg "trusted_proxies|X-Forwarded" --context 3

Repository: makeplane/plane

Length of output: 1770


🏁 Script executed:

# Check if there's any rate_limit usage or configuration elsewhere in the web app
rg "rate_limit" ./apps/web/

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check if the PR or any related docs mention rate limiting intent
find . -name "*.md" -type f | xargs rg -l "rate" 2>/dev/null | head -10

Repository: makeplane/plane

Length of output: 432


🏁 Script executed:

# Check for any rate_limit directives in Caddyfiles
rg "rate_limit" ./apps/*/caddy/

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check git history or comments around the xcaddy ratelimit addition
git log --oneline --all -- "**/Dockerfile.web" | head -20

Repository: makeplane/plane

Length of output: 1365


🏁 Script executed:

# Look at proxy Caddyfile to understand the architecture better
cat -n ./apps/proxy/Caddyfile.ce

Repository: makeplane/plane

Length of output: 1099


Revisit trusted_proxies scope and consider wiring the ratelimit module

  • trusted_proxies static 0.0.0.0 treats all clients as trusted proxies, allowing any source to supply X-Forwarded-For / related headers that Caddy will trust. While the web app is behind the proxy layer, this setting is unnecessarily permissive; restrict it to your actual proxy ranges (e.g., trusted_proxies static 127.0.0.1).
  • The Docker image builds Caddy with github.com/mholt/caddy-ratelimit, but no rate_limit directives are configured in this Caddyfile. Either add rate limiting if needed or remove the unused module from the build to reduce image size.
🤖 Prompt for AI Agents
In apps/web/caddy/Caddyfile around lines 1–17, the trusted_proxies setting is
overly permissive (static 0.0.0.0) and the caddy-ratelimit module is built but
not used; restrict trusted_proxies to only your actual proxy ranges (for example
127.0.0.1 and any internal Docker/CNI subnet or load‑balancer CIDRs) so Caddy
only trusts X-Forwarded-* from those sources, and either add appropriate
rate_limit directives (site or global scope) to throttle abusive requests using
the built-in ratelimit syntax or remove the github.com/mholt/caddy-ratelimit
build tag from the Docker build to avoid shipping an unused module — update the
Caddyfile to reflect restricted CIDRs and, if adding rate limiting, configure
sensible limits and scopes for your app endpoints.

Comment on lines 75 to 95
# *****************************************************************************
# STAGE 3: Serve with nginx
# STAGE 3: Serve with Caddy
# *****************************************************************************
FROM nginx:1.27-alpine AS production

COPY apps/web/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=installer /app/apps/web/build/client /usr/share/nginx/html
FROM caddy:2.11-builder-alpine AS caddy-builder

EXPOSE 3000
RUN xcaddy build \
--with github.com/mholt/caddy-ratelimit

FROM caddy:2.11-alpine AS production

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

COPY apps/web/caddy/Caddyfile /etc/caddy/Caddyfile

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1:3000/ >/dev/null || exit 1

CMD ["nginx", "-g", "daemon off;"]
COPY --from=installer /app/apps/web/build/client /usr/share/caddy/html

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
Copy link
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:

# First, let's locate and read the full Dockerfile.web to understand all stages
find . -name "Dockerfile.web" -type f

Repository: makeplane/plane

Length of output: 84


🏁 Script executed:

# Also find the admin Dockerfile mentioned in the comment
find . -name "Dockerfile.admin" -o -name "Dockerfile" | grep -i admin | head -5

Repository: makeplane/plane

Length of output: 88


🏁 Script executed:

# Read the full Dockerfile.web
cat -n ./apps/web/Dockerfile.web

Repository: makeplane/plane

Length of output: 3779


🏁 Script executed:

# Read the admin Dockerfile for comparison
cat -n ./apps/admin/Dockerfile.admin

Repository: makeplane/plane

Length of output: 3708


🏁 Script executed:

# Check the difference between the two Dockerfiles for the Caddy stage
diff -u <(sed -n '79,95p' ./apps/web/Dockerfile.web) <(sed -n '80,98p' ./apps/admin/Dockerfile.admin)

Repository: makeplane/plane

Length of output: 146


🏁 Script executed:

# Verify curl availability in caddy images by checking Docker documentation or image details
web_search "caddy:2.11-alpine curl available tools"

Repository: makeplane/plane

Length of output: 109


🏁 Script executed:

# Check if there's any Docker-related documentation or comments in the repo
find . -type f \( -name "*.md" -o -name "*.txt" \) -exec grep -l "caddy\|curl" {} \; | head -10

Repository: makeplane/plane

Length of output: 96


🌐 Web query:

caddy:2.11-alpine image what tools included curl

💡 Result:

Short answer: No — the official caddy:2.11-alpine image is a minimal Alpine-based image and does not include curl by default. You can add it with: apk add --no-cache curl. [1][2][3]

Sources:

  • Official caddy Docker repo (alpine images are minimal). [1]
  • Notes that default/small Caddy images omit utilities like curl; alpine variant only adds basic shell/utilities. [2]
  • Alpine documentation/examples: curl is not installed by default; install via apk. [3]

🏁 Script executed:

# Also check the Caddyfile to understand what endpoints are expected
cat -n ./apps/web/caddy/Caddyfile

Repository: makeplane/plane

Length of output: 400


Fix Caddy binary copy source and address curl/EXPOSE issues

  • COPY --from=builder /usr/bin/caddy /usr/bin/caddy on line 86 is incorrect: the builder stage is the Node build image (node:22-alpine) and does not contain a caddy binary. The compiled binary with the ratelimit plugin is in the caddy-builder stage. The Docker build will fail or silently omit the ratelimit plugin. Change to:
-COPY --from=builder /usr/bin/caddy /usr/bin/caddy
+COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
  • The HEALTHCHECK on lines 90–91 uses curl, which is not present in the caddy:2.11-alpine base image. Either install curl via RUN apk add --no-cache curl or use an alternative healthcheck command (e.g., caddy list-modules or a different probe).

  • Add EXPOSE 3000 before the final CMD to match the admin Dockerfile and document the exposed port.

  • Lines 79 and 81 have unnecessary 2-space indentation; align them to column 0 for consistency with standard Dockerfile style.

📝 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
# *****************************************************************************
# STAGE 3: Serve with nginx
# STAGE 3: Serve with Caddy
# *****************************************************************************
FROM nginx:1.27-alpine AS production
COPY apps/web/nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=installer /app/apps/web/build/client /usr/share/nginx/html
FROM caddy:2.11-builder-alpine AS caddy-builder
EXPOSE 3000
RUN xcaddy build \
--with github.com/mholt/caddy-ratelimit
FROM caddy:2.11-alpine AS production
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
COPY apps/web/caddy/Caddyfile /etc/caddy/Caddyfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1:3000/ >/dev/null || exit 1
CMD ["nginx", "-g", "daemon off;"]
COPY --from=installer /app/apps/web/build/client /usr/share/caddy/html
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
# *****************************************************************************
# STAGE 3: Serve with Caddy
# *****************************************************************************
FROM caddy:2.11-builder-alpine AS caddy-builder
RUN xcaddy build \
--with github.com/mholt/caddy-ratelimit
FROM caddy:2.11-alpine AS production
COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
COPY apps/web/caddy/Caddyfile /etc/caddy/Caddyfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1:3000/ >/dev/null || exit 1
COPY --from=installer /app/apps/web/build/client /usr/share/caddy/html
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile"]
🤖 Prompt for AI Agents
In apps/web/Dockerfile.web around lines 75 to 95, the Dockerfile incorrectly
copies the Caddy binary from the wrong stage and has portability/style issues:
change the COPY source to use the caddy-builder stage (COPY --from=caddy-builder
/usr/bin/caddy /usr/bin/caddy), fix the two lines with extra two-space
indentation so they start at column 0, address the HEALTHCHECK using curl which
is not present in the caddy:2.11-alpine image by either installing curl (e.g.,
add a RUN apk add --no-cache curl in the production stage) or replace the check
with a curl-free probe such as running a Caddy CLI check (e.g., caddy
list-modules) or a simple socket probe, and add EXPOSE 3000 before the final CMD
to document and expose the port.

Copy link
Member

@prateekshourya29 prateekshourya29 left a comment

Choose a reason for hiding this comment

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

@pratapalakshmi, we can also delete the nginx.conf file for all apps. We also have it for space app but it's not being used as well.

- Deleted nginx.conf files as part of the migration to Caddy.
- Updated Dockerfile to reflect changes in the Caddy build process.
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.

2 participants