Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 0.15.4 #2096

Merged
merged 24 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
051f439
Add `key` for custom favicon
ajnart May 15, 2024
5043b9c
Refactor favicon links in Meta components
ajnart May 15, 2024
84f57bb
chore: add test:docker script for local development
ajnart May 15, 2024
2bf4231
Merge pull request #2052 from ajnart/1676-custom-favicon-does-not-red…
ajnart May 16, 2024
b7fb086
Add New Language Arabic (#2051)
bo3bdo May 18, 2024
cb2b28c
feat: add environment variable puid and pgid #2011
nyok1912 May 23, 2024
afdca50
feat: add dns disable timer (#2029)
hillaliy May 23, 2024
dfe7b0d
chore: new crowdin updates (#2038)
ajnart May 23, 2024
68ff84c
feat: add indexers site hyperlink (#2061)
hillaliy May 25, 2024
c042c24
feat: add Proxmox Uptime View (#2092)
JasonLeeB06 Aug 1, 2024
3de04bd
fix: #2086 enforce password requirements for admin user (#2098)
manuel-rw Aug 4, 2024
fbc099d
fix: #1707 refech interval app pings (#2097)
manuel-rw Aug 4, 2024
76d46ec
refactor: improve variable names
Meierschlumpf Aug 8, 2024
47c4011
fix: removing a category hides items from the wrapper below (#2103)
Meierschlumpf Aug 8, 2024
cc240f4
docs: update README.md (#2104)
cospeedster Aug 12, 2024
a879358
feat: add download functionality for board configuration files (#2111)
Meierschlumpf Aug 24, 2024
858ccd7
⬆️ Update dependency axios to v1.7.4 [SECURITY] (#2106)
renovate[bot] Aug 24, 2024
c008cc9
New Crowdin updates (#2059)
ajnart Aug 24, 2024
5b5121a
config: fix arm64 build
manuel-rw Aug 24, 2024
995c8a4
fix: dev docker issues (#2118)
Meierschlumpf Aug 30, 2024
eba3010
fix: notebook widget overflow on mobile (#2112)
joser93 Aug 30, 2024
d2a40dd
fix: revert pgid puid docker changes (#2119)
Meierschlumpf Sep 1, 2024
714936a
chore: new Crowdin updates (#2117)
ajnart Sep 1, 2024
6a7532b
chore: increase package.json version
manuel-rw Sep 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ NEXTAUTH_SECRET="anything"
# Disable analytics
NEXT_PUBLIC_DISABLE_ANALYTICS="true"

DEFAULT_COLOR_SCHEME="light"
DEFAULT_COLOR_SCHEME="light"
125 changes: 96 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,33 +1,106 @@
FROM node:20.2.0-slim
FROM node:20.2.0-slim as compiler

#RUN apt-get update && apt-get -y install git wget openssl

WORKDIR /app

# Define node.js environment variables
#RUN git clone https://github.com/ajnart/homarr.git .
COPY . .

RUN yarn install
COPY .env.example .env
RUN yarn build


FROM node:20.2.0-alpine3.18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Careful with Alpine as a base image. Ensure that you're actually getting worthwhile benefits by using it, it's often a source of troubleshooting gotchas that are not fun to debug (memory leaks, poor performance, DNS, glibc vs musl, are all known issues projects have had with Alpine).

It's typically chosen for the smaller size and attribution of that being more secure as a smaller attack surface. Just be sure you're not giving too much of a trade-off with that choice when you can get fairly competitive sized images by using alternatives when this matters.


Fedora for example can install a minimal image with dnf --installroot, although for NodeJS that is 124MB, Alpine is about half of that. For context:

  • node:slim debian image you're using in the build stage is 217 MB.
  • node:alpine is 154MB.

So Fedora is probably a win for you regardless size wise, while the --installroot approach would minimize deps (no package manager, minimal deps).

Similar to Alpine, fedora releases are a bit more frequently than Debian, but Fedora releases do get updates to packages over time too (vs Debian releases which besides age don't tend to upgrade packages much between releases beyond security fixes). Whereas your node images have the benefit of being pinned (you can pin the package with dnf too).

The Docker image support isn't that critical to the project. Since you're already familiar with Debian / apt, perhaps stay with that and optionally take advantage of the image suggestion below (Debian too) as the final stage.


That said you could try Google's debian based distroless NodeJS image too:

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    wagoodman/dive:latest gcr.io/distroless/nodejs22-debian12:latest

image

It weighs in at 143MB and as you can see above is quite minimal for internal contents. This is for a final stage image only that you COPY your build into as there is no other programs to run, nor package manager available.

For security benefits that should serve you much better and keep things fairly simple.

#ARGS is only for build

ARG PORT=7575

# Keep free id >= 1000 for user, under node:x image by default node user uses 1000:1000
ARG NODE_UID=800

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bulk of this I commented on from the original PR, and discourage the approach taken.

There is a feature with Docker that should arrive in future for the concern with better mapping container writes to volume mounts with different UID/GID. Usually the issue for users is just inconvenience if they want to interact with the files from the host, as otherwise they could keep it in a named data volume which avoids some issues (docker cp lets you easily transfer between host and container too).

I haven't looked at the issue that motivated these changes with PUID/PGID, but I assume it's not actually breaking functionality.

ARG NODE_GID=800

#PUID can be set during build and run time
ARG PUID=801
ARG PGID=801

#it must be the same as the host, temporary 802 or any, automatically changed at runtime
ARG DOCKER_GID=802

#By default, ping group using gid 999, keep free to possible docker host gid
ARG PING_GID=803

# Expose the default application port
EXPOSE $PORT
ENV PORT=${PORT}

# Define node.js environment variables
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production
ENV NODE_OPTIONS '--no-experimental-fetch'

COPY next.config.js ./
COPY public ./public
COPY package.json ./temp_package.json
COPY yarn.lock ./temp_yarn.lock
# App environment variables
ENV DATABASE_URL "file:/app/data/db.sqlite"
ENV NEXTAUTH_URL "http://localhost:7575"
ENV NEXTAUTH_SECRET NOT_IN_USE_BECAUSE_JWTS_ARE_UNUSED

# Must be same as host user when using bind mount volumes
ENV PUID $PUID
ENV PGID $PGID

RUN apk update && apk add --no-cache \
supervisor docker-cli shadow

RUN usermod -u $NODE_UID node
RUN groupmod -g $NODE_GID node

RUN groupmod -g $PING_GID ping

# Creating local homarr user and group
RUN groupadd -g $PGID homarr
RUN useradd homarr -u $PUID -g homarr --home-dir /app --shell /sbin/nologin
RUN usermod -aG node homarr

# Creating a local Docker group and add docker group to homarr user
RUN groupadd -g $DOCKER_GID docker
RUN usermod -aG docker homarr

# Enable sudo for homarr user, only for debug and testing purposes
#RUN apk add sudo
#RUN echo "homarr ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

# Configure entrypoint
COPY ./docker/entrypoint /
RUN chmod +x /entrypoint.sh
RUN chmod +x /docker-entrypoint.d/*.sh

# Configure supervisord
COPY ./docker/etc/supervisord.conf /etc/supervisord.conf
COPY ./docker/etc/supervisor /etc/supervisor

#RUN chown homarr:homarr /app
USER node
WORKDIR /app

COPY --from=compiler --chown=node:homarr /app/next.config.js ./
COPY --from=compiler --chown=node:homarr /app/public ./public
COPY --from=compiler --chown=node:homarr /app/package.json ./temp_package.json
COPY --from=compiler --chown=node:homarr /app/yarn.lock ./temp_yarn.lock
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY .next/standalone ./
COPY .next/static ./.next/static
COPY ./scripts/run.sh ./scripts/run.sh
RUN chmod +x ./scripts/run.sh
COPY ./drizzle ./drizzle

COPY ./drizzle/migrate ./migrate
COPY ./tsconfig.json ./migrate/tsconfig.json
COPY ./cli ./cli
COPY --from=compiler --chown=node:homarr /app/.next/standalone ./
COPY --from=compiler --chown=node:homarr /app/.next/static ./.next/static

RUN mkdir /data
COPY --from=compiler --chown=node:homarr /app/scripts/run.sh ./scripts/run.sh
RUN chmod +x ./scripts/run.sh
COPY --from=compiler --chown=node:homarr /app/drizzle ./drizzle

# Install dependencies
RUN apt update && apt install -y openssl wget
COPY --from=compiler --chown=node:homarr /app/drizzle/migrate ./migrate
COPY --from=compiler --chown=node:homarr /app/tsconfig.json ./migrate/tsconfig.json
COPY --from=compiler --chown=node:homarr /app/cli ./cli

# Move node_modules to temp location to avoid overwriting
RUN mv node_modules _node_modules
Expand All @@ -45,22 +118,16 @@ RUN mv node_modules ./migrate/node_modules
# Copy temp node_modules of app to app folder
RUN mv _node_modules node_modules

RUN echo '#!/bin/bash\nnode /app/cli/cli.js "$@"' > /usr/bin/homarr
RUN chmod +x /usr/bin/homarr
RUN cd /app/cli && yarn --immutable

# Expose the default application port
EXPOSE $PORT
ENV PORT=${PORT}
# Root is needed for supervisord
USER root

ENV DATABASE_URL "file:/data/db.sqlite"
ENV NEXTAUTH_URL "http://localhost:7575"
ENV PORT 7575
ENV NEXTAUTH_SECRET NOT_IN_USE_BECAUSE_JWTS_ARE_UNUSED
RUN echo '#!/bin/bash\nnode /app/cli/cli.js "$@"' > /usr/bin/homarr
RUN chmod +x /usr/bin/homarr

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use an ENTRYPOINT ["/path/to/node", "/app/cli/cli.js"] instead, the "$@" is unnecessary as you get this functionality from ENTRYPOINT with the syntax shown, it will append CMD to it, or whatever a user provides as an override to CMD at runtime.

If you use the distroless image suggestion, you don't have to worry about the ENTRYPOINT, it'll point to node already, but they suggest to put the args into CMD, which may be a slight breaking change to the image if someone was providing custom args to cli.js.

HEALTHCHECK --interval=10s --timeout=5s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT} || exit 1

VOLUME [ "/app/data/configs" ]
VOLUME [ "/data" ]
ENTRYPOINT ["sh", "./scripts/run.sh"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there was talking about reverting such changes. I do want to point out that you should avoid VOLUME directives here.

They create anonymous data volumes per container instance. Those will accumulate with each container created unless the container is disposed of (eg: docker run --rm ... instead of just docker run ...).

It is worse for Docker Compose which will be more common in usage, as destroying containers does not remove these anonymous volumes they're tied to the project and service name, thus if you did for some reason want to rely on this implicit persistence it's more difficult to reset/discard it should the need arise (docker volumes ls is really unhelpful for identifying which anonymous volume belongs to what container).

Instead it's much better to just document these locations, either in proper docs or a compose.yaml example (don't use docker-compose.yml btw, official Docker docs are aligned with compose.yaml since 2023Q3 with Compose v2 release being deemed stable).

Users that want to persist data should provide explicit config to indicate that, otherwise so long as a container is not destroyed it will persist any filesystem state it has when stopped/restarted (sometimes a source of bugs), whereas VOLUME tends to just cause more problems than benefits.

ENTRYPOINT [ "/entrypoint.sh" ]
CMD []
23 changes: 23 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: "2.1"
services:
#---------------------------------------------------------------------#
# Homarr - A simple, yet powerful dashboard for your server. #
#---------------------------------------------------------------------#
homarr:
container_name: homarr
#image: ghcr.io/ajnart/homarr:latest
build: # only for dev branch...
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- DOCKER_GID=999 # Must be same as host docker group id
- DATABASE_URL=file:/app/data/configs/db.sqlite
volumes:
- /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration
- ./homarr_persistence/configs:/app/data/configs
- ./homarr_persistence/icons:/app/public/icons
ports:
- '7575:7575'
26 changes: 26 additions & 0 deletions docker/entrypoint/docker-entrypoint.d/00-user-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

HOMARR_USER_PATHS="/app/data /app/public/icons"

for path in $HOMARR_USER_PATHS
do
if [ ! -d "$path" ]; then
mkdir -p $path
fi

find $path ! -user $PUID -print0 | while read -d $'\0' FILE
do
echo "${FILE} is not own by current user, fixing..."
chown $PUID:$PGID ${FILE}
done
done

echo Setting homarr UID to $PUID and GID to $PGID please wait...
usermod -u $PUID homarr
groupmod -g $PGID homarr

DOCKER_GID=$(stat -c %g /var/run/docker.sock 2>/dev/null)
if [[ $? -eq 0 ]]; then
echo "SETTING DOCKER GID TO ${DOCKER_GID}"
groupmod -g $DOCKER_GID docker
fi
68 changes: 68 additions & 0 deletions docker/entrypoint/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/sh
# vim:sw=4:ts=4:et

set -e
echo "Entering entrypoint..."

echo "Param \$1: $1"
echo "User: "$(whoami)


entrypoint_log() {
if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
echo "$@"
fi
}

if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"

entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/"
find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do
case "$f" in
*.envsh)
if [ -x "$f" ]; then
entrypoint_log "$0: Sourcing $f";
. "$f"
else
# warn on shell scripts without exec bit
entrypoint_log "$0: Ignoring $f, not executable";
fi
;;
*.sh)
if [ -x "$f" ]; then
entrypoint_log "$0: Launching $f";
"$f"
else
# warn on shell scripts without exec bit
entrypoint_log "$0: Ignoring $f, not executable";
fi
;;
*) entrypoint_log "$0: Ignoring $f";;
esac
done

entrypoint_log "$0: Configuration complete; ready for start up"
else
entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration"
fi

#exec "$@"

# sys container init:
#
# If no command is passed to the container, supervisord becomes init and
# starts all its configured programs (per /etc/supervisord.conf).
#
# If a command is passed to the container, it runs in the foreground;
# supervisord runs in the background and starts all its configured
# programs.
#
# In either case, supervisord always starts its configured programs.

if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
exec supervisord -n "$@"
else
supervisord -c /etc/supervisord.conf &
exec "$@"
fi
13 changes: 13 additions & 0 deletions docker/etc/supervisor/conf.d/homarr.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[program:homarr]
command=/app/scripts/run.sh
environment=HOME="/app",USER="homarr",LOGNAME="homarr"
user=homarr
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=true
startretries=0
stopasgroup=true
killasgroup=true
stopsignal=KILL
Loading
Loading