Alpine's built-in 'users' group owns GID 100 and 'nobody' owns UID 99.
The old check tested by name (appgroup/appuser) which always passed,
then hit 'addgroup: gid 100 in use' on creation.
Now checks by GID/UID via getent — reuses the existing group/user if
the ID is already taken, only creates new ones when the ID is free.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed the conditional COPY line that used 2>/dev/null || true —
shell operators are not valid in Dockerfile COPY instructions and
were being interpreted as literal paths. pnpm hoists all deps to
root node_modules via shamefully-hoist so the line was unnecessary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>