Deploying Nostra Server (API) as a Stateless Container
Because we use a local SDK via .yalc, simply copying `package.json` isn't enough. We
must copy the hidden .yalc folder into the container.
FROM node:20-alpine
WORKDIR /app
# 1. Copy Manifests and Local Libs
COPY package.json yarn.lock ./
COPY .yalc ./.yalc
# 2. Install Dependencies (Frozen for stability)
RUN yarn install --frozen-lockfile
# 3. Copy Source Code
COPY . .
# 4. Generate Prisma Client (Required for DB access)
RUN npx prisma generate
# 5. Build TypeScript code
RUN yarn build
# 6. Configure Environment
ENV NODE_ENV=production
ENV PORT=8080
# 7. Start
CMD ["node", "dist/index.js"]
Google Cloud Run needs to pull the image from a registry usually Google Artifact Registry (GAR).
# 1. Set your Project ID
export PROJECT_ID="your-gcp-project-id"
# 2. Enable Artifact Registry API (First time only)
gcloud services enable artifactregistry.googleapis.com
# 3. Create a Docker Repository (First time only)
gcloud artifacts repositories create nostra-repo \
--repository-format=docker \
--location=us-central1 \
--description="Nostra Docker Repository"
# 4. Configure Docker to use gcloud credentials
gcloud auth configure-docker us-central1-docker.pkg.dev
# 5. Build and Push (Submitting build to Cloud Build is easiest)
# Run this from /nostra-server/api directory
gcloud builds submit --tag us-central1-docker.pkg.dev/$PROJECT_ID/nostra-repo/nostra-api:latest .
Using gcloud builds submit is easier than building locally because it avoids
architecture issues (e.g., building on Apple Silicon M1/M2 for Linux servers).
We need to tell Cloud Run about our database. The safest way is using the Cloud SQL Proxy integration.
gcloud run deploy nostra-api \
--image us-central1-docker.pkg.dev/$PROJECT_ID/nostra-repo/nostra-api:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars="NODE_ENV=production" \
--set-secrets="DATABASE_URL=projects/$PROJECT_ID/secrets/DB_URL/versions/1"
For efficient Docker images, we use Next.js standalone mode. We also need to copy
the .yalc folder here as well.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
// ... other config
};
module.exports = nextConfig;
FROM node:20-alpine AS builder
WORKDIR /app
# 1. Copy Manifests and Local Libs
COPY package.json yarn.lock ./
COPY .yalc ./.yalc
# 2. Install Dependencies
RUN yarn install --frozen-lockfile
# 3. Copy Source
COPY . .
# 4. Build (Creates .next/standalone)
RUN yarn build
# --- Runner Stage (Small Image) ---
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=8080
# 5. Copy Standalone Build
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 8080
CMD ["node", "server.js"]
# 1. Build Web Image
gcloud builds submit --tag us-central1-docker.pkg.dev/$PROJECT_ID/nostra-repo/nostra-web:latest .
# 2. Deploy Web Service
gcloud run deploy nostra-web \
--image us-central1-docker.pkg.dev/$PROJECT_ID/nostra-repo/nostra-web:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars="NEXT_PUBLIC_API_URL=https://nostra-api-uc.a.run.app"
Note: Set NEXT_PUBLIC_API_URL to the URL of your deployed API service
(from Step 3).