The portal includes a production-ready Dockerfile that creates an optimized container image using multi-stage builds.
Quick Start
Build Docker Image
Build the container image:docker build -t agrospai-portal .
Run Container
Run the container with environment variables:docker run -p 3000:3000 \
-e NEXT_PUBLIC_INFURA_PROJECT_ID="your_infura_id" \
-e NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID="your_wc_id" \
-e AGROPORTAL_API_KEY="your_api_key" \
agrospai-portal
Dockerfile Architecture
The Dockerfile uses a multi-stage build process for optimal image size and security:
Stage 1: Dependencies
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat git
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci --ignore-scripts; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
This stage:
- Installs only production dependencies
- Supports multiple package managers (npm, yarn, pnpm)
- Uses Alpine Linux for minimal size
- Requires libc6-compat for Node.js native modules
Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
This stage:
- Copies node_modules from deps stage
- Builds the Next.js application
- Disables Next.js telemetry
- Creates standalone output
Stage 3: Runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
This stage:
- Creates a non-root user for security
- Copies only necessary build artifacts
- Runs as unprivileged user (nextjs)
- Exposes port 3000
- Starts the Next.js server
Environment Variables
Environment variables can be passed to the container in several ways:
Using Command Line
docker run -p 3000:3000 \
-e NEXT_PUBLIC_INFURA_PROJECT_ID="xxx" \
-e NEXT_PUBLIC_METADATACACHE_URI="https://aquarius.pontus-x.eu" \
-e AGROPORTAL_API_KEY="xxx" \
agrospai-portal
Using Environment File
Create a .env.production file:
NEXT_PUBLIC_INFURA_PROJECT_ID=your_infura_id
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_wc_id
NEXT_PUBLIC_METADATACACHE_URI=https://aquarius.pontus-x.eu
AGROPORTAL_API_KEY=your_api_key
NEXT_TELEMETRY_DISABLED=1
Then run:
docker run -p 3000:3000 --env-file .env.production agrospai-portal
Docker Compose
For more complex deployments, use Docker Compose:
version: '3.8'
services:
portal:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- NEXT_TELEMETRY_DISABLED=1
- NEXT_PUBLIC_INFURA_PROJECT_ID=${INFURA_PROJECT_ID}
- NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=${WALLETCONNECT_PROJECT_ID}
- NEXT_PUBLIC_METADATACACHE_URI=https://aquarius.pontus-x.eu
- AGROPORTAL_API_KEY=${AGROPORTAL_API_KEY}
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Run with:
Advanced Configuration
Custom Port Mapping
Map container port 3000 to a different host port:
docker run -p 8080:3000 agrospai-portal
Access at http://localhost:8080
Volume Mounting for Logs
Mount a volume for persistent logs:
docker run -p 3000:3000 \
-v $(pwd)/logs:/app/logs \
agrospai-portal
Resource Limits
Set memory and CPU limits:
docker run -p 3000:3000 \
--memory="2g" \
--cpus="1.5" \
agrospai-portal
Production Best Practices
Never include sensitive environment variables in the Dockerfile. Always pass them at runtime.
Security Hardening
- Run as non-root user - Already configured in Dockerfile
- Use specific image tags - Pin Node.js version:
node:20-alpine
- Scan for vulnerabilities:
docker scan agrospai-portal
- Use secrets management:
docker run -p 3000:3000 \
--secret infura_key \
--secret agroportal_key \
agrospai-portal
Multi-Architecture Builds
Build for multiple platforms:
docker buildx build --platform linux/amd64,linux/arm64 \
-t agrospai-portal:latest .
Health Checks
Add health check to container:
docker run -p 3000:3000 \
--health-cmd="wget --quiet --tries=1 --spider http://localhost:3000 || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
agrospai-portal
Container Registry
Push to Registry
Tag and push to your container registry:
docker tag agrospai-portal your-username/agrospai-portal:latest
docker push your-username/agrospai-portal:latest
Troubleshooting
Check logs:
docker logs agrospai-portal
Build Fails
Clean Docker build cache:
docker builder prune
docker build --no-cache -t agrospai-portal .
Port Already in Use
Find and stop conflicting container:
docker ps
docker stop <container-id>
Environment Variables Not Working
NEXT_PUBLIC_* variables must be set at build time. Pass them using --build-arg:
docker build \
--build-arg NEXT_PUBLIC_INFURA_PROJECT_ID=xxx \
-t agrospai-portal .
Next Steps
Production Build
Learn about the production build process
Hosting Options
Alternative deployment methods