fix: Add entrypoint script to fix volume permissions at runtime

- Add docker-entrypoint.sh that runs as root to fix mounted volume permissions
- Creates required subdirectories (logs, users, packages) before app starts
- Copies default config.js if missing
- Drops to node user via su-exec before running the app
- Update Dockerfile to use entrypoint and install su-exec
- Update docker-compose.yml with UID/GID mapping and separate volume mounts
- Wrap filesystem operations in try-catch to handle permission errors gracefully
This commit is contained in:
2025-12-29 19:02:58 -08:00
parent fc2190c7cd
commit ecb9fe6b33
5 changed files with 51 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
FROM node:20-alpine
RUN apk add --no-cache --virtual=build-dependencies build-base git python3-dev py3-setuptools && \
apk add --no-cache yarn
apk add --no-cache yarn su-exec
WORKDIR /var/opt/hardlounge-src
ENV THELOUNGE_HOME /var/opt/hardlounge
COPY --chown=node:node package.json yarn.lock .
@@ -13,8 +13,9 @@ RUN NODE_ENV=production yarn build && \
ln -s /var/opt/hardlounge-src/index.js /var/opt/hardlounge-src/hardlounge
USER root
RUN apk del --purge build-dependencies && \
mkdir -p /var/opt/hardlounge && \
mkdir -p /var/opt/hardlounge/logs /var/opt/hardlounge/users /var/opt/hardlounge/packages && \
chown -R node:node /var/opt/hardlounge
USER node
COPY --chmod=755 docker-entrypoint.sh /docker-entrypoint.sh
EXPOSE 9000
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/var/opt/hardlounge-src/hardlounge", "start"]

View File

@@ -2,7 +2,10 @@ services:
hardlounge:
image: git.supernets.org/supernets/hardlounge:latest
build: .
user: "${UID}:${GID}"
ports:
- "9000:9000"
volumes:
- "$PWD/config:/var/opt/hardlounge"
- ./data/logs:/var/opt/hardlounge/logs
- ./data/packages:/var/opt/hardlounge/packages
- ./config:/var/opt/hardlounge

19
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
set -e
# Fix permissions on mounted volume at runtime
chown -R node:node /var/opt/hardlounge 2>/dev/null || true
chmod -R 755 /var/opt/hardlounge 2>/dev/null || true
# Create required subdirectories
mkdir -p /var/opt/hardlounge/logs /var/opt/hardlounge/users /var/opt/hardlounge/packages 2>/dev/null || true
chown -R node:node /var/opt/hardlounge 2>/dev/null || true
# Copy default config if it doesn't exist
if [ ! -f /var/opt/hardlounge/config.js ]; then
cp /var/opt/hardlounge-src/dist/defaults/config.js /var/opt/hardlounge/config.js 2>/dev/null || true
chown node:node /var/opt/hardlounge/config.js 2>/dev/null || true
fi
# Run as node user
exec su-exec node "$@"

View File

@@ -22,26 +22,37 @@ program
function initalizeConfig() {
if (!fs.existsSync(Config.getConfigPath())) {
fs.mkdirSync(Config.getHomePath(), {recursive: true});
try {
fs.chmodSync(Config.getHomePath(), "0700");
} catch (e) {
// Ignore chmod errors (e.g., on mounted volumes)
}
fs.mkdirSync(Config.getHomePath(), {recursive: true});
fs.copyFileSync(
path.resolve(path.join(__dirname, "..", "..", "defaults", "config.js")),
Config.getConfigPath()
);
log.info(`Configuration file created at ${colors.green(Config.getConfigPath())}.`);
try {
fs.chmodSync(Config.getHomePath(), "0700");
} catch (e) {
// Ignore chmod errors (e.g., on mounted volumes)
}
fs.copyFileSync(
path.resolve(path.join(__dirname, "..", "..", "defaults", "config.js")),
Config.getConfigPath()
);
log.info(`Configuration file created at ${colors.green(Config.getConfigPath())}.`);
} catch (e) {
log.error(
"Unable to create config directory/file. Please ensure the config volume is writable or pre-create config.js"
);
process.exit(1);
}
}
try {
fs.mkdirSync(Config.getUsersPath(), {recursive: true, mode: 0o700});
} catch (e) {
// Ignore permission errors on mounted volumes
fs.mkdirSync(Config.getUsersPath(), {recursive: true});
try {
fs.mkdirSync(Config.getUsersPath(), {recursive: true});
} catch (e2) {
// Ignore if this also fails
}
}
}

View File

@@ -276,7 +276,7 @@ class Config {
try {
fs.mkdirSync(userLogsPath, {recursive: true, mode: 0o750});
} catch (e: any) {
log.error("Unable to create logs directory", e);
log.warn("Unable to create logs directory, logging to disk may not work");
}
} else if (logsStat && logsStat.mode & 0o001) {
log.warn(