diff --git a/Dockerfile b/Dockerfile index 2443239..997d721 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,7 @@ ENV NODE_ENV=production RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs COPY --from=build /app/.next/standalone ./ COPY --from=build /app/.next/static ./.next/static +COPY --from=build /app/public ./public # Copy schema.sql needed by migration 001_init at runtime COPY --from=build /app/src/lib/schema.sql ./src/lib/schema.sql # Create data directory with correct ownership for SQLite diff --git a/docker-compose.yml b/docker-compose.yml index a0e27ab..fb53bc9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,12 +21,12 @@ services: - NET_BIND_SERVICE security_opt: - no-new-privileges:true + pids_limit: 256 deploy: resources: limits: memory: 512M cpus: '1.0' - pids: 256 networks: - mc-net restart: unless-stopped diff --git a/src/lib/__tests__/docker-compose-schema.test.ts b/src/lib/__tests__/docker-compose-schema.test.ts new file mode 100644 index 0000000..ad9525f --- /dev/null +++ b/src/lib/__tests__/docker-compose-schema.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest' +import { readFileSync } from 'fs' +import { resolve } from 'path' + +/** + * Tests that docker-compose.yml and Dockerfile contain the expected + * configuration for Compose v5+ compatibility and complete runtime assets. + */ + +const ROOT = resolve(__dirname, '../../..') + +describe('docker-compose.yml schema', () => { + const content = readFileSync(resolve(ROOT, 'docker-compose.yml'), 'utf-8') + + it('uses service-level pids_limit instead of deploy.resources.limits.pids', () => { + // pids_limit should be at service level (not nested inside deploy) + expect(content).toContain('pids_limit:') + + // Should NOT have pids inside deploy.resources.limits + const deployBlock = content.match(/deploy:[\s\S]*?(?=\n\s{4}\w|\nvolumes:|\nnetworks:)/)?.[0] ?? '' + expect(deployBlock).not.toContain('pids:') + }) + + it('still has memory and cpus in deploy.resources.limits', () => { + expect(content).toContain('memory:') + expect(content).toContain('cpus:') + }) +}) + +describe('Dockerfile runtime stage', () => { + const content = readFileSync(resolve(ROOT, 'Dockerfile'), 'utf-8') + + it('copies public directory to runtime stage', () => { + expect(content).toContain('COPY --from=build /app/public ./public') + }) + + it('copies standalone output', () => { + expect(content).toContain('COPY --from=build /app/.next/standalone ./') + }) + + it('copies static assets', () => { + expect(content).toContain('COPY --from=build /app/.next/static ./.next/static') + }) + + it('copies schema.sql for migrations', () => { + expect(content).toContain('schema.sql') + }) +})