Skip to content

Commit e75effc

Browse files
authored
Merge pull request #257 from pSpitzner/docker_tweaks
Added UV to the base image, so that we can use `uv pip install` in prod Thanks to @edgar-vincent
2 parents 6f57510 + 5eb2e9a commit e75effc

File tree

4 files changed

+80
-55
lines changed

4 files changed

+80
-55
lines changed

docker/Dockerfile

Lines changed: 68 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# ------------------------------ ffmpeg builder ------------------------------ #
1+
# ------------------------------ Builder ffmpeg ------------------------------ #
2+
23
# FFMPEG comes with a ton of dependencies (e.g. llvm)
34
# the full install is over 400mb...
45
# we use a static version which is only 50mb
@@ -18,53 +19,18 @@ ARG TARGETARCH
1819
RUN curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${TARGETARCH}-static.tar.xz \
1920
| tar -xJ -C /tmp/ffmpeg --strip-components=1
2021

21-
# -------------------------------- Base image -------------------------------- #
22-
FROM python:3.12-slim-trixie AS base
23-
24-
ENV HOSTNAME="beets-container"
25-
ENV EDITOR="vi"
26-
# need to set some cli editor so `beet edit` works, vi comes with slim
27-
ENV BEETSDIR="/config/beets"
28-
ENV BEETSFLASKDIR="/config/beets-flask"
29-
ENV BEETSFLASKLOG="/logs/beets-flask.log"
30-
31-
# Create user and group
32-
RUN groupadd -g 1000 beetle && \
33-
useradd -m -u 1000 -g beetle beetle
34-
35-
# map beets directory and our configs to /config
36-
RUN mkdir -p /config/beets /config/beets-flask /logs && \
37-
chown -R beetle:beetle /config /logs
38-
39-
# our default folders they should not be used in production
40-
RUN mkdir -p /music/inbox /music/imported && \
41-
chown -R beetle:beetle /music
42-
43-
# Install dependencies:
44-
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
45-
apt-get update && \
46-
apt-get install -y --no-install-recommends \
47-
redis \
48-
tmux \
49-
imagemagick && \
50-
rm -rf /var/lib/apt/lists/*
51-
52-
# Copy only the binaries from builder
53-
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffmpeg /usr/local/bin/ffmpeg
54-
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffprobe /usr/local/bin/ffprobe
55-
RUN ffmpeg -version
56-
5722

5823
# ------------------------------ Builder python ------------------------------ #
24+
5925
FROM ghcr.io/astral-sh/uv:python3.12-trixie-slim AS builder_py
6026
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
6127

6228
WORKDIR /repo/backend
63-
ENV PYTHONUNBUFFERED=1 \
64-
UV_COMPILE_BYTECODE=1 \
65-
UV_LINK_MODE=copy \
66-
UV_NO_DEV=1 \
67-
UV_PYTHON_DOWNLOADS=0
29+
ENV PYTHONUNBUFFERED=1
30+
ENV UV_COMPILE_BYTECODE=1
31+
ENV UV_LINK_MODE=copy
32+
ENV UV_NO_DEV=1
33+
ENV UV_PYTHON_DOWNLOADS=0
6834

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

8753
# ------------------------------- Builder node ------------------------------- #
54+
8855
FROM node:22-slim AS builder_node
8956

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

10774

75+
# ---------------------------------------------------------------------------- #
76+
# Base #
77+
# ---------------------------------------------------------------------------- #
78+
79+
FROM python:3.12-slim-trixie AS base
80+
COPY --from=builder_py /bin/uv /bin/uvx /bin/
81+
82+
ENV HOSTNAME="beets-container"
83+
ENV EDITOR="vi"
84+
# need to set some cli editor so `beet edit` works, vi comes with slim
85+
ENV BEETSDIR="/config/beets"
86+
ENV BEETSFLASKDIR="/config/beets-flask"
87+
ENV BEETSFLASKLOG="/logs/beets-flask.log"
88+
ENV PATH="/repo/backend/.venv/bin:$PATH"
89+
90+
# Create user and group
91+
RUN groupadd -g 1000 beetle && \
92+
useradd -m -u 1000 -g beetle beetle
93+
94+
# map beets directory and our configs to /config
95+
RUN mkdir -p /config/beets /config/beets-flask /logs && \
96+
chown -R beetle:beetle /config /logs
97+
98+
# our default folders they should not be used in production
99+
RUN mkdir -p /music/inbox /music/imported && \
100+
chown -R beetle:beetle /music
101+
102+
# Install dependencies:
103+
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
104+
apt-get update && \
105+
apt-get install -y --no-install-recommends \
106+
redis \
107+
tmux \
108+
imagemagick && \
109+
rm -rf /var/lib/apt/lists/*
110+
111+
# Copy only the binaries from builder
112+
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffmpeg /usr/local/bin/ffmpeg
113+
COPY --from=builder_ffmpeg /tmp/ffmpeg/ffprobe /usr/local/bin/ffprobe
114+
RUN ffmpeg -version
115+
116+
# Remove pip to avoid confusion (force users to use `uv pip install`)
117+
RUN rm -f /usr/local/bin/pip /usr/local/bin/pip3
118+
RUN rm -rf /root/.cache/pip
119+
RUN echo '#!/bin/sh\n\
120+
echo "Beets-Flask relies on uv for package management. Please avoid pip and use:"\n\
121+
echo "uv pip install <package>"\n\
122+
exit 1' > /usr/local/bin/pip
123+
RUN chmod 755 /usr/local/bin/pip
124+
RUN chown root:root /usr/local/bin/pip
125+
RUN ln -sf /usr/local/bin/pip /usr/local/bin/pip3
126+
108127
# ------------------------------------------------------------------------------------ #
109128
# Production #
110129
# ------------------------------------------------------------------------------------ #
130+
111131
FROM base AS prod
112132

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

124144
USER root
125-
ENV PATH="/repo/backend/.venv/bin:$PATH"
126145

127146
ENTRYPOINT [ \
128147
"/bin/bash", "-c", \
@@ -137,20 +156,21 @@ ENTRYPOINT [ \
137156
# ------------------------------------------------------------------------------------ #
138157

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

160+
ENV IB_SERVER_CONFIG="dev_docker"
161+
ENV UV_LINK_MODE=copy
143162

144163
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
145164
apt-get update && \
146165
apt-get install -y --no-install-recommends \
147166
curl \
148-
build-essential && \
167+
build-essential && \
149168
rm -rf /var/lib/apt/lists/*
150169

151170
# Install nodejs
152171
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
153172
RUN apt-get install -y nodejs
173+
154174
# Install pnpm
155175
RUN npm install --global corepack@latest
156176
RUN corepack enable pnpm
@@ -160,21 +180,19 @@ RUN corepack use pnpm@latest-10
160180
WORKDIR /repo
161181
COPY ./frontend/package.json ./frontend/pnpm-lock.yaml /repo/frontend/
162182
COPY ./backend/pyproject.toml ./backend/uv.lock /repo/backend/
163-
WORKDIR /repo/frontend
164183

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

170-
ENV IB_SERVER_CONFIG="dev_docker"
171-
172190
# relies on mounting this volume
173191
WORKDIR /repo
174192
USER root
175193
ENTRYPOINT [ \
176194
"/bin/bash", "-c", \
177-
"./docker/entrypoints/entrypoint_fix_permissions.sh && \
195+
"./docker/entrypoints/entrypoint_fix_permissions.sh && \
178196
./docker/entrypoints/entrypoint_user_scripts.sh && \
179197
su beetle -c ./docker/entrypoints/entrypoint_dev.sh" \
180198
]

docker/entrypoints/entrypoint_dev.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export FLASK_DEBUG=1
4343
cd /repo/backend
4444

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

4848
redis-server --daemonize yes
4949

docker/entrypoints/entrypoint_user_scripts.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ fi
1919
# check for requirements.txt
2020
if [ -f /config/requirements.txt ]; then
2121
log "Installing pip requirements from /config/requirements.txt"
22-
pip install -r /config/requirements.txt
22+
uv pip install -r /config/requirements.txt
2323
fi
2424
if [ -f /config/beets-flask/requirements.txt ]; then
2525
log "Installing pip requirements from /config/beets-flask/requirements.txt"
26-
pip install -r /config/beets-flask/requirements.txt
26+
uv pip install -r /config/beets-flask/requirements.txt
2727
fi

docs/plugins.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ Plugin support is experimental.
88
Installing beets plugins varies depending on the particular plugin.
99
[See the official docs](https://docs.beets.io/en/latest/plugins/index.html).
1010

11-
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.
11+
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.
1212

13-
On startup, the container will run the startup script if it exists, and afterwards install the requirements from the `requirements.txt` file using pip.
13+
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/).
14+
15+
```{note}
16+
We use uv to manage python dependecies in a virtual environment at `/repo/backend/.venv`.
17+
This should by default be activated already (`which python`), but note that, to install
18+
more dependencies you need to use `uv pip install`. A normal `pip install` will not place
19+
packages at the right location.
20+
```
1421

1522
## Example startup.sh: keyfinder
1623

0 commit comments

Comments
 (0)