Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
118 changes: 68 additions & 50 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# ------------------------------ ffmpeg builder ------------------------------ #
# ------------------------------ Builder ffmpeg ------------------------------ #

# FFMPEG comes with a ton of dependencies (e.g. llvm)
# the full install is over 400mb...
# we use a static version which is only 50mb
Expand All @@ -18,53 +19,18 @@ ARG TARGETARCH
RUN curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${TARGETARCH}-static.tar.xz \
| tar -xJ -C /tmp/ffmpeg --strip-components=1

# -------------------------------- Base image -------------------------------- #
FROM python:3.12-slim-trixie AS base

ENV HOSTNAME="beets-container"
ENV EDITOR="vi"
# need to set some cli editor so `beet edit` works, vi comes with slim
ENV BEETSDIR="/config/beets"
ENV BEETSFLASKDIR="/config/beets-flask"
ENV BEETSFLASKLOG="/logs/beets-flask.log"

# Create user and group
RUN groupadd -g 1000 beetle && \
useradd -m -u 1000 -g beetle beetle

# map beets directory and our configs to /config
RUN mkdir -p /config/beets /config/beets-flask /logs && \
chown -R beetle:beetle /config /logs

# our default folders they should not be used in production
RUN mkdir -p /music/inbox /music/imported && \
chown -R beetle:beetle /music

# Install dependencies:
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
apt-get update && \
apt-get install -y --no-install-recommends \
redis \
tmux \
imagemagick && \
rm -rf /var/lib/apt/lists/*

# Copy only the binaries from builder
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffmpeg /usr/local/bin/ffmpeg
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffprobe /usr/local/bin/ffprobe
RUN ffmpeg -version


# ------------------------------ Builder python ------------------------------ #

FROM ghcr.io/astral-sh/uv:python3.12-trixie-slim AS builder_py
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /repo/backend
ENV PYTHONUNBUFFERED=1 \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_NO_DEV=1 \
UV_PYTHON_DOWNLOADS=0
ENV PYTHONUNBUFFERED=1
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
ENV UV_NO_DEV=1
ENV UV_PYTHON_DOWNLOADS=0

# Install backend dependencies
COPY ./backend/pyproject.toml ./backend/uv.lock /repo/backend/
Expand All @@ -85,6 +51,7 @@ RUN mkdir -p /version
RUN python -c "import tomllib; print(tomllib.load(open('/repo/backend/pyproject.toml', 'rb'))['project']['version'])" > /version/backend.txt

# ------------------------------- Builder node ------------------------------- #

FROM node:22-slim AS builder_node

# Install pnpm
Expand All @@ -105,9 +72,62 @@ RUN node -p "require('/repo/frontend/package.json').version" > /version/frontend
RUN pnpm run build


# ---------------------------------------------------------------------------- #
# Base #
# ---------------------------------------------------------------------------- #

FROM python:3.12-slim-trixie AS base
COPY --from=builder_py /bin/uv /bin/uvx /bin/

ENV HOSTNAME="beets-container"
ENV EDITOR="vi"
# need to set some cli editor so `beet edit` works, vi comes with slim
ENV BEETSDIR="/config/beets"
ENV BEETSFLASKDIR="/config/beets-flask"
ENV BEETSFLASKLOG="/logs/beets-flask.log"
ENV PATH="/repo/backend/.venv/bin:$PATH"

# Create user and group
RUN groupadd -g 1000 beetle && \
useradd -m -u 1000 -g beetle beetle

# map beets directory and our configs to /config
RUN mkdir -p /config/beets /config/beets-flask /logs && \
chown -R beetle:beetle /config /logs

# our default folders they should not be used in production
RUN mkdir -p /music/inbox /music/imported && \
chown -R beetle:beetle /music

# Install dependencies:
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
apt-get update && \
apt-get install -y --no-install-recommends \
redis \
tmux \
imagemagick && \
rm -rf /var/lib/apt/lists/*

# Copy only the binaries from builder
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffmpeg /usr/local/bin/ffmpeg
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffprobe /usr/local/bin/ffprobe
RUN ffmpeg -version

# Remove pip to avoid confusion (force users to use `uv pip install`)
RUN rm -f /usr/local/bin/pip /usr/local/bin/pip3
RUN rm -rf /root/.cache/pip
RUN echo '#!/bin/sh\n\
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Smart!

echo "Beets-Flask relies on uv for package management. Please avoid pip and use:"\n\
echo "uv pip install <package>"\n\
exit 1' > /usr/local/bin/pip
RUN chmod 755 /usr/local/bin/pip
RUN chown root:root /usr/local/bin/pip
RUN ln -sf /usr/local/bin/pip /usr/local/bin/pip3

# ------------------------------------------------------------------------------------ #
# Production #
# ------------------------------------------------------------------------------------ #

FROM base AS prod

ENV IB_SERVER_CONFIG="prod"
Expand All @@ -122,7 +142,6 @@ COPY --chown=beetle:beetle ./docker/entrypoints/*.sh /repo/
RUN chmod +x /repo/*.sh

USER root
ENV PATH="/repo/backend/.venv/bin:$PATH"

ENTRYPOINT [ \
"/bin/bash", "-c", \
Expand All @@ -137,20 +156,21 @@ ENTRYPOINT [ \
# ------------------------------------------------------------------------------------ #

FROM base AS dev
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV UV_LINK_MODE=copy

ENV IB_SERVER_CONFIG="dev_docker"
ENV UV_LINK_MODE=copy

RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
apt-get update && \
apt-get install -y --no-install-recommends \
curl \
build-essential && \
build-essential && \
rm -rf /var/lib/apt/lists/*

# Install nodejs
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
RUN apt-get install -y nodejs

# Install pnpm
RUN npm install --global corepack@latest
RUN corepack enable pnpm
Expand All @@ -160,21 +180,19 @@ RUN corepack use pnpm@latest-10
WORKDIR /repo
COPY ./frontend/package.json ./frontend/pnpm-lock.yaml /repo/frontend/
COPY ./backend/pyproject.toml ./backend/uv.lock /repo/backend/
WORKDIR /repo/frontend

# Extract version from package.json
WORKDIR /repo/frontend
RUN mkdir -p /version
RUN node -p "require('/repo/frontend/package.json').version" > /version/frontend.txt
RUN python -c "import tomllib; print(tomllib.load(open('/repo/backend/pyproject.toml', 'rb'))['project']['version'])" > /version/backend.txt

ENV IB_SERVER_CONFIG="dev_docker"

# relies on mounting this volume
WORKDIR /repo
USER root
ENTRYPOINT [ \
"/bin/bash", "-c", \
"./docker/entrypoints/entrypoint_fix_permissions.sh && \
"./docker/entrypoints/entrypoint_fix_permissions.sh && \
./docker/entrypoints/entrypoint_user_scripts.sh && \
su beetle -c ./docker/entrypoints/entrypoint_dev.sh" \
]
Expand Down
2 changes: 1 addition & 1 deletion docker/entrypoints/entrypoint_dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export FLASK_DEBUG=1
cd /repo/backend

uv sync --locked
source .venv/bin/activate
# No need to activate, we have this in PATH

redis-server --daemonize yes

Expand Down
4 changes: 2 additions & 2 deletions docker/entrypoints/entrypoint_user_scripts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ fi
# check for requirements.txt
if [ -f /config/requirements.txt ]; then
log "Installing pip requirements from /config/requirements.txt"
pip install -r /config/requirements.txt
uv pip install -r /config/requirements.txt
fi
if [ -f /config/beets-flask/requirements.txt ]; then
log "Installing pip requirements from /config/beets-flask/requirements.txt"
pip install -r /config/beets-flask/requirements.txt
uv pip install -r /config/beets-flask/requirements.txt
fi
11 changes: 9 additions & 2 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ Plugin support is experimental.
Installing beets plugins varies depending on the particular plugin.
[See the official docs](https://docs.beets.io/en/latest/plugins/index.html).

We might automate this in the future, but for now you can place a `requirements.txt` and/or `startup.sh` in either the `/config` folder or `/config/beets-flask` folder. The `requirements.txt` may include [python dependencies](https://pip.pypa.io/en/stable/reference/requirements-file-format/), and the `startup.sh` file may be an executable shell script that is compatible with the container's alpine linux base.
We might automate this in the future, but for now you can place a `requirements.txt` and/or `startup.sh` in either the `/config` folder or `/config/beets-flask` folder. The `requirements.txt` may include [python dependencies](https://pip.pypa.io/en/stable/reference/requirements-file-format/), and the `startup.sh` file may be an executable shell script that is compatible with the container's debian linux base.

On startup, the container will run the startup script if it exists, and afterwards install the requirements from the `requirements.txt` file using pip.
On startup, the container will run the startup script if it exists, and afterwards install the requirements from the `requirements.txt` file using [uv](https://docs.astral.sh/uv/pip/).

```{note}
We use uv to manage python dependecies in a virtual environment at `/repo/backend/.venv`.
This should by default be activated already (`which python`), but note that, to install
more dependencies you need to use `uv pip install`. A normal `pip install` will not place
packages at the right location.
```

## Example startup.sh: keyfinder

Expand Down