fix: don't crash at boot when running under a UID with no passwd entry
os.userInfo() throws ERR_SYSTEM_ERROR (uv_os_get_passwd) for a UID that has no /etc/passwd entry, which is exactly what `docker run --user 1234:1234` produces — the very workaround the README recommends. Combined with the switch to a non-root image user, this crashed the server on startup for any deployment that needed a custom UID to read its archives. Also document the non-root default and the /cache index volume. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uBWhwV3wFQ5MBCcMHHem7
This commit is contained in:
co-authored by
Claude Opus 5
parent
53703cd7cd
commit
55b0752b05
@@ -54,10 +54,27 @@ If the app shows "No Archives Found" and logs `EACCES: permission denied`:
|
|||||||
chmod -R 755 /path/to/archives
|
chmod -R 755 /path/to/archives
|
||||||
```
|
```
|
||||||
2. **SELinux (Fedora/RHEL/CentOS)**: Use the `:z` flag in your volume mount as shown above.
|
2. **SELinux (Fedora/RHEL/CentOS)**: Use the `:z` flag in your volume mount as shown above.
|
||||||
3. **User Mapping**: You can force the container to run as your host user:
|
3. **User Mapping**: The container runs as the non-root `node` user (UID 1000).
|
||||||
|
If your archives are readable only by another account, run as that user
|
||||||
|
instead — the container needs to *list* the archive directory, so `--x`
|
||||||
|
(traverse-only) permissions are not enough:
|
||||||
```bash
|
```bash
|
||||||
docker run --user $(id -u):$(id -g) ...
|
docker run --user $(stat -c '%u:%g' /path/to/archives) ...
|
||||||
```
|
```
|
||||||
|
In Compose:
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
instaarchive:
|
||||||
|
user: "1234:1234" # a UID that can read your archives
|
||||||
|
```
|
||||||
|
|
||||||
|
### Archive Index
|
||||||
|
|
||||||
|
On first start the server walks the archive root once and caches the result,
|
||||||
|
keyed by directory mtime. This matters on network storage: for a 110k-file
|
||||||
|
archive root, listing went from ~52s per request to ~0.1s. Mount a volume at
|
||||||
|
`/cache` (or set `CACHE_DIR`) so the index survives restarts, otherwise it is
|
||||||
|
rebuilt on every start.
|
||||||
|
|
||||||
## Supported Archive Structure
|
## Supported Archive Structure
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,23 @@ const PORT = process.env.PORT || 3001;
|
|||||||
const ARCHIVES_DIR = path.resolve(process.env.ARCHIVES_DIR || path.join(__dirname, '_sample-archives'));
|
const ARCHIVES_DIR = path.resolve(process.env.ARCHIVES_DIR || path.join(__dirname, '_sample-archives'));
|
||||||
|
|
||||||
console.log(`[Server] Initializing...`);
|
console.log(`[Server] Initializing...`);
|
||||||
console.log(`[Server] Running as user: ${os.userInfo().username} (UID: ${os.userInfo().uid}, GID: ${os.userInfo().gid})`);
|
/**
|
||||||
|
* Describe the running user without assuming it exists in /etc/passwd.
|
||||||
|
*
|
||||||
|
* `os.userInfo()` throws ENOENT for a UID with no passwd entry, which is
|
||||||
|
* exactly what happens when the container is started with `--user 1234:1234`
|
||||||
|
* (as the deployment docs suggest) — previously crashing the server at boot.
|
||||||
|
*/
|
||||||
|
const describeUser = () => {
|
||||||
|
try {
|
||||||
|
const info = os.userInfo();
|
||||||
|
return `${info.username} (UID: ${info.uid}, GID: ${info.gid})`;
|
||||||
|
} catch {
|
||||||
|
return `UID: ${typeof process.getuid === 'function' ? process.getuid() : '?'}, GID: ${typeof process.getgid === 'function' ? process.getgid() : '?'} (no passwd entry)`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`[Server] Running as user: ${describeUser()}`);
|
||||||
console.log(`[Server] Environment ARCHIVES_DIR: ${process.env.ARCHIVES_DIR}`);
|
console.log(`[Server] Environment ARCHIVES_DIR: ${process.env.ARCHIVES_DIR}`);
|
||||||
console.log(`[Server] Resolved ARCHIVES_DIR: ${ARCHIVES_DIR}`);
|
console.log(`[Server] Resolved ARCHIVES_DIR: ${ARCHIVES_DIR}`);
|
||||||
|
|
||||||
@@ -114,7 +130,7 @@ app.get('/api/archives', (req, res) => {
|
|||||||
res.json(archives);
|
res.json(archives);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.code === 'EACCES') {
|
if (err.code === 'EACCES') {
|
||||||
console.error(`[API] Permission Denied! The server (UID ${os.userInfo().uid}) cannot read ${ARCHIVES_DIR}.`);
|
console.error(`[API] Permission Denied! The server (${describeUser()}) cannot read ${ARCHIVES_DIR}.`);
|
||||||
console.error(`[API] Hint: If using Linux/Docker, check folder permissions (chmod 755) or SELinux context (append :z to your volume mount).`);
|
console.error(`[API] Hint: If using Linux/Docker, check folder permissions (chmod 755) or SELinux context (append :z to your volume mount).`);
|
||||||
} else {
|
} else {
|
||||||
console.error('[API] Error listing archives:', err);
|
console.error('[API] Error listing archives:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user