Adding support to build and run auto_rfp in a docker container (#38)

* consolidate duplicate Next.js config files into TypeScript version

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add Dockerfile

* Add the docker scripts to package.json

* Add docker doc to the README

* Update the README with the env file changes/requirements

* Update README with build- vs. run-time configuration requirements

* Ignore a/the .devcontainer (if there is one)

* Adding more docker scripts

* Adding a health endpoint to check the health of the container

* Make sure we can hit the health endpoint without authentication

* Make docker-build work

* Add more health-check script

* Moving docker config to next.config.ts

* Fix docker-run script. Remove network host access.

---------

Co-authored-by: Roland Tritsch <roland@tritsch.email>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Roland Tritsch
2025-12-26 16:51:58 +00:00
committed by GitHub
parent ba8cd12233
commit 7f93393f10
8 changed files with 127 additions and 31 deletions
+1
View File
@@ -0,0 +1 @@
.devcontainer
+52
View File
@@ -0,0 +1,52 @@
# Stage 1: Builder
FROM node:20-alpine AS builder
# Install pnpm and required dependencies
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
RUN apk add --no-cache libc6-compat openssl
WORKDIR /app
# Copy package files and install dependencies
COPY package.json pnpm-lock.yaml ./
COPY prisma ./prisma/
RUN pnpm install --frozen-lockfile
# Generate Prisma client
RUN pnpm prisma generate
# Copy application files and build
COPY . .
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN pnpm build
# Stage 2: Runner
FROM node:20-alpine AS runner
RUN apk add --no-cache openssl
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=3000 \
HOSTNAME="0.0.0.0"
# Copy necessary files from builder
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
CMD ["node", "server.js"]
+28 -9
View File
@@ -70,26 +70,28 @@ Create a `.env` file in the root directory:
```bash
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/auto_rfp"
DIRECT_URL="postgresql://username:password@localhost:5432/auto_rfp"
DATABASE_URL=postgresql://username:password@localhost:5432/auto_rfp
DIRECT_URL=postgresql://username:password@localhost:5432/auto_rfp
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL="your-supabase-project-url"
NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key"
NEXT_PUBLIC_SUPABASE_URL=<your-supabase-project-url>
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-supabase-anon-key>
# OpenAI API
OPENAI_API_KEY="your-openai-api-key"
OPENAI_API_KEY=<your-openai-api-key>
# LlamaCloud
LLAMACLOUD_API_KEY="your-llamacloud-api-key"
LLAMACLOUD_API_KEY=<your-llamacloud-api-key>
# Optional: Internal API key and domain for internal users
# LLAMACLOUD_API_KEY_INTERNAL="your-internal-llamacloud-api-key"
# INTERNAL_EMAIL_DOMAIN="@yourdomain.com" # Defaults to @runllama.ai
# LLAMACLOUD_API_KEY_INTERNAL=<your-internal-llamacloud-api-key>
# INTERNAL_EMAIL_DOMAIN=<your-domain> # Defaults to @runllama.ai
# App Configuration
NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL=http://localhost:3000
```
**Note**: To use the env file for the app AND for docker the env var values cannot be in quotes.
### 4. Database Setup
#### Set up PostgreSQL Database
@@ -241,6 +243,22 @@ The application can be deployed to any platform that supports Node.js:
- AWS Amplify
- Google Cloud Run
### Build and Run with Docker
AutoRFP includes Docker support for containerized deployment.
```bash
# Build the Docker image
pnpm docker-build
# Run the container
pnpm docker-run
```
**Note:** The Docker container uses Next.js standalone output mode for optimized production deployment. Make sure your `.env.local` includes a database connection string that's accessible from within the Docker container.
**IMPORTANT NOTE**: When you build/run a docker container all of the `NEXT_*` env vars are resolved at `build-time` (when the container is built; because the vars also need to be available in the frontend and are generated into the frontend code). The other vars are resolved at `run-time` (when the container is started). Means, if you (for instance) need to set `NEXT_PUBLIC_APP_URL` to your own site (e.g. `https://rfp.mydomain.com`) then you need to make this change to the env file, before you run docker-build.
## 🔌 API Endpoints
### Core APIs
@@ -351,6 +369,7 @@ This project is licensed under the MIT License - see the [LICENSE][] file for de
Built with ❤️ using Next.js, LlamaIndex, and OpenAI
[Docker]: https://docs.docker.com/get-docker/
[LICENSE]: ./LICENSE
[cloud.llamaindex.ai]: https://cloud.llamaindex.ai
[http://localhost:3000]: http://localhost:3000
+22
View File
@@ -0,0 +1,22 @@
import { NextRequest } from "next/server";
import { apiHandler } from "@/lib/middleware/api-handler";
import { db } from "@/lib/db";
import { env } from "@/lib/env";
/**
* Health check endpoint for Docker and monitoring
* GET /api/health
*/
export async function GET(request: NextRequest) {
return apiHandler(async () => {
// Check database connectivity
await db.$queryRaw`SELECT 1`;
return {
status: "healthy",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
environment: process.env.NODE_ENV,
};
});
}
+2 -1
View File
@@ -40,7 +40,8 @@ export async function updateSession(request: NextRequest) {
if (
!user &&
!request.nextUrl.pathname.startsWith('/login') &&
!request.nextUrl.pathname.startsWith('/auth')
!request.nextUrl.pathname.startsWith('/auth') &&
!request.nextUrl.pathname.startsWith('/api/health')
) {
// no user, potentially respond by redirecting the user to the login page
const url = request.nextUrl.clone()
-17
View File
@@ -1,17 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// Configure environment variables that will be available on both server and client side
env: {
// You will need to set LLAMACLOUD_API_KEY in your deployment environment
// or in a .env file that's not committed to version control
LLAMACLOUD_API_KEY: process.env.LLAMACLOUD_API_KEY,
// Internal API key for internal users
LLAMACLOUD_API_KEY_INTERNAL: process.env.LLAMACLOUD_API_KEY_INTERNAL,
// Internal email domain (defaults to @runllama.ai)
INTERNAL_EMAIL_DOMAIN: process.env.INTERNAL_EMAIL_DOMAIN,
},
// Other Next.js config options
reactStrictMode: true,
}
module.exports = nextConfig;
+14 -1
View File
@@ -1,7 +1,20 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
// Configure environment variables that will be available on both server and client side
env: {
// You will need to set LLAMACLOUD_API_KEY in your deployment environment
// or in a .env.local file that's not committed to version control
LLAMACLOUD_API_KEY: process.env.LLAMACLOUD_API_KEY,
// Internal API key for internal users
LLAMACLOUD_API_KEY_INTERNAL: process.env.LLAMACLOUD_API_KEY_INTERNAL,
// Internal email domain (defaults to @runllama.ai)
INTERNAL_EMAIL_DOMAIN: process.env.INTERNAL_EMAIL_DOMAIN,
},
// Other Next.js config options
reactStrictMode: true,
// Enable standalone output for Docker deployment
output: 'standalone',
};
export default nextConfig;
+8 -3
View File
@@ -3,11 +3,16 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"dev": "next dev --turbopack",
"docker-build": "docker build --tag auto-rfp .",
"docker-log": "docker logs --follow auto-rfp",
"docker-run": "docker run --rm --detach --env-file .env --name auto-rfp auto-rfp",
"docker-stop": "docker stop auto-rfp",
"health-check": "curl http://localhost:3000/api/health",
"lint": "next lint",
"postinstall": "prisma generate"
"postinstall": "prisma generate",
"start": "next start"
},
"dependencies": {
"@ai-sdk/openai": "^1.3.22",