# EasyPanel Deployment Standard

> **Scope:** nextjs-supabase
> **Layer:** 2 (on keyword)
> **Keywords:** easypanel, docker, deploy, deployment, container
> **Load When:** easypanel or docker deployment keywords detected

Stack: Next.js 15 + Supabase + .NET Backend

## Core Rules

- ALWAYS use multi-stage Docker builds for minimal image size
- ALWAYS configure health checks for zero-downtime deploys
- NEVER hardcode secrets in Dockerfiles -- use EasyPanel environment variables
- ALWAYS use `.dockerignore` to exclude node_modules, .git, .env files
- SSL is automatic via Let's Encrypt -- no manual certificate management

## .NET 10 Dockerfile

```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
RUN adduser --disabled-password --gecos "" appuser
USER appuser
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
```

## Next.js Standalone Dockerfile

```dockerfile
FROM node:22-alpine AS base

FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 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
USER nextjs
EXPOSE 3000
ENV PORT=3000 HOSTNAME="0.0.0.0"
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
CMD ["node", "server.js"]
```

Required: `output: "standalone"` in `next.config.ts`.

## .dockerignore

```
node_modules
.next
.git
.env*
*.md
.vscode
coverage
test
__tests__
```

## EasyPanel Service Config

| Setting | .NET Backend | Next.js Frontend |
|---------|-------------|-----------------|
| Source | GitHub | GitHub |
| Build method | Dockerfile | Dockerfile |
| Dockerfile path | `./backend/Dockerfile` | `./frontend/Dockerfile` |
| Port | 8080 | 3000 |
| Domain | api.example.com | app.example.com |

### GitHub Integration

1. Generate GitHub PAT with `repo` scope
2. EasyPanel: Settings > GitHub > Add token
3. Select repository and branch
4. Enable "Auto Deploy" for webhook-triggered deploys

### Domain and SSL

DNS setup (CNAME to EasyPanel server):
```
app.example.com  CNAME  your-server.easypanel.host
api.example.com  CNAME  your-server.easypanel.host
```

SSL via Let's Encrypt is automatic. Force HTTPS enabled by default.

## Environment Variables

### .NET Backend

```env
ASPNETCORE_ENVIRONMENT=Production
ASPNETCORE_URLS=http://+:8080
ConnectionStrings__DefaultConnection=Host=...;Database=...;Username=...;Password=...
Supabase__Url=https://xxx.supabase.co
Supabase__ServiceRoleKey=eyJ...
Supabase__JwtSecret=your-jwt-secret
```

### Next.js Frontend

```env
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
NEXT_PUBLIC_API_URL=https://api.example.com
```

## Health Check Endpoints

```csharp
// .NET: Program.cs
builder.Services.AddHealthChecks()
    .AddNpgSql(connectionString, name: "database");
app.MapHealthChecks("/health");
```

```ts
// Next.js: app/api/health/route.ts
export async function GET() {
  return Response.json({ status: "healthy", timestamp: new Date().toISOString() });
}
```

## Zero-Downtime Deploys

EasyPanel uses Docker HEALTHCHECK to determine container readiness:

1. New container starts alongside old container
2. Health check passes after start-period + retries
3. Traffic shifts to new container
4. Old container stopped

| HEALTHCHECK Param | Value | Purpose |
|-------------------|-------|---------|
| `--interval` | 30s | Time between checks |
| `--timeout` | 5s | Max response wait |
| `--start-period` | 10s | Startup grace period |
| `--retries` | 3 | Failures before unhealthy |

## Monitoring

- **Logs**: Real-time in EasyPanel UI
- **Metrics**: CPU, memory, network via dashboard
- **Restart**: Auto-restart on crash (default)

Structured logging:

```csharp
// .NET: Serilog with JSON output
builder.Host.UseSerilog((ctx, cfg) => cfg
    .ReadFrom.Configuration(ctx.Configuration)
    .WriteTo.Console(new JsonFormatter()));
```

```ts
// Next.js: pino
import pino from "pino";
const logger = pino({ level: process.env.LOG_LEVEL ?? "info" });
```

## Deployment Checklist

| Step | Action |
|------|--------|
| 1 | Verify `output: "standalone"` in next.config.ts |
| 2 | Test Docker build locally |
| 3 | Test health endpoint |
| 4 | Configure env vars in EasyPanel |
| 5 | Set up custom domain + DNS |
| 6 | Verify SSL certificate |
| 7 | Enable auto-deploy from GitHub |
| 8 | Push to main, verify deployment |
