11# syntax=docker/dockerfile:1
22
3- FROM ghcr.io/linuxserver/unrar:latest AS unrar
4- FROM ghcr.io/linuxserver/baseimage-ubuntu:noble
5-
6- # Set the default shell for the following RUN instructions to bash instead of sh
7- SHELL ["/bin/bash" , "-c" ]
8-
93# Simple Example Build Command:
104# docker build \
115# --tag crocodilestick/calibre-web-automated:dev \
@@ -23,18 +17,17 @@ SHELL ["/bin/bash", "-c"]
2317# --build-arg="VERSION=2.1.0" \
2418# --tag crocodilestick/calibre-web-automated:latest .
2519
26- ARG BUILD_DATE
27- ARG VERSION
28- ARG CALIBREWEB_RELEASE=0.6.24
20+ # ============================================================================
21+ # STAGE 1: Dependencies - Install system packages and Python dependencies
22+ # ============================================================================
23+ FROM ghcr.io/linuxserver/baseimage-ubuntu:noble AS dependencies
24+
2925ARG CALIBRE_RELEASE=8.9.0
3026ARG KEPUBIFY_RELEASE=v4.0.4
31- LABEL build_version="Version:- ${VERSION}"
32- LABEL build_date="${BUILD_DATE}"
33- LABEL CW-base-version="${CALIBREWEB_RELEASE}"
34- LABEL maintainer="CrocodileStick"
3527
36- # Copy local files into the container
37- COPY --chown=abc:abc . /app/calibre-web-automated/
28+ # Set the default shell for the following RUN instructions to bash instead of sh
29+ SHELL ["/bin/bash" , "-c" ]
30+
3831# STEP 1 - Install Required Packages
3932RUN \
4033 # STEP 1.1 - Add deadsnakes PPA for Python 3.13 and install required apt packages
6760 nano \
6861 sqlite3 \
6962 zip && \
63+ # STEP 1.2 - Install additional Calibre required packages
64+ apt-get install -y --no-install-recommends \
65+ libxtst6 \
66+ libxrandr2 \
67+ libxkbfile1 \
68+ libxcomposite1 \
69+ libopengl0 \
70+ libnss3 \
71+ libxkbcommon0 \
72+ libegl1 \
73+ libxdamage1 \
74+ libgl1 \
75+ libglx-mesa0 \
76+ xz-utils \
77+ binutils && \
7078 # Install lsof 4.99.5 from source to fix hanging issue with 4.95 (issue #654)
7179 echo "**** install lsof 4.99.5 from source ****" && \
7280 LSOF_VERSION="4.99.5" && \
@@ -83,53 +91,30 @@ RUN \
8391 # Create python3 symlink to point to python3.13
8492 ln -sf /usr/bin/python3.13 /usr/bin/python3 && \
8593 # Install pip for Python 3.13
86- curl -sS https://bootstrap.pypa.io/get-pip.py | python3.13 && \
87- # STEP 1.2 - Set up a python virtual environment and install pip and wheel packages
88- cd /app/calibre-web-automated && \
94+ curl -sS https://bootstrap.pypa.io/get-pip.py | python3.13
95+
96+ # STEP 2 - Set up Python virtual environment
97+ RUN \
8998 python3.13 -m venv /lsiopy && \
9099 /lsiopy/bin/pip install -U --no-cache-dir \
91100 pip \
92- wheel && \
93- # STEP 1.3 - Installing the required python packages listed in 'requirements.txt' and 'optional-requirements.txt'
101+ wheel
102+
103+ # STEP 3 - Copy requirements files and install Python packages
104+ # Copy only requirements files first to leverage Docker layer caching
105+ COPY --chown=abc:abc requirements.txt optional-requirements.txt /app/calibre-web-automated/
106+
107+ RUN \
108+ # STEP 3.1 - Installing the required python packages listed in 'requirements.txt' and 'optional-requirements.txt'
94109 # HOWEVER, they are not pulled from PyPi directly, they are pulled from linuxserver's Ubuntu Wheel Index
95110 # This is essentially a repository of precompiled some of the most popular packages with C/C++ source code
96111 # This provides the install maximum compatibility with multiple different architectures including: x86_64, armv71 and aarch64
97112 # You can read more about python wheels here: https://realpython.com/python-wheels/
98113 /lsiopy/bin/pip install -U --no-cache-dir --find-links https://wheel-index.linuxserver.io/ubuntu/ -r \
99- requirements.txt -r optional-requirements.txt && \
100- # STEP 2 - Move contents of /app/calibre-web-automated/root to / and delete the /app/calibre-web-automated/root directory
101- cp -R /app/calibre-web-automated/root/* / && \
102- rm -R /app/calibre-web-automated/root/ && \
103- # STEP 3 - Run CWA install script to make required dirs, set script permissions and add aliases for CLI commands ect.
104- chmod +x /app/calibre-web-automated/scripts/setup-cwa.sh && \
105- /app/calibre-web-automated/scripts/setup-cwa.sh && \
106- # STEP 4 - Create koplugin.zip from KOReader plugin folder
107- echo "~~~~ Creating koplugin.zip from KOReader plugin folder... ~~~~" && \
108- if [ -d "/app/calibre-web-automated/koreader/plugins/cwasync.koplugin" ]; then \
109- cd /app/calibre-web-automated/koreader/plugins && \
110- # Calculate digest of all files in the plugin for debugging purposes
111- echo "Calculating digest of plugin files..." && \
112- PLUGIN_DIGEST=$(find cwasync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) && \
113- echo "Plugin digest: $PLUGIN_DIGEST" && \
114- # Create a file named after the digest inside the plugin folder
115- echo "Plugin files digest: $PLUGIN_DIGEST" > cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
116- echo "Build date: $(date)" >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
117- echo "Files included:" >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
118- find cwasync.koplugin -type f -name "*.lua" -o -name "*.json" | sort >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
119- zip -r koplugin.zip cwasync.koplugin/ && \
120- echo "Created koplugin.zip from cwasync.koplugin folder with digest file: ${PLUGIN_DIGEST}.digest" ; \
121- else \
122- echo "Warning: cwasync.koplugin folder not found, skipping zip creation" ; \
123- fi && \
124- # STEP 4.1 - Move koplugin.zip to static directory
125- if [ -f "/app/calibre-web-automated/koreader/plugins/koplugin.zip" ]; then \
126- mkdir -p /app/calibre-web-automated/cps/static && \
127- cp /app/calibre-web-automated/koreader/plugins/koplugin.zip /app/calibre-web-automated/cps/static/ && \
128- echo "Moved koplugin.zip to static directory" ; \
129- else \
130- echo "Warning: koplugin.zip not found, skipping move to static directory" ; \
131- fi && \
132- # STEP 5 - Installs kepubify
114+ /app/calibre-web-automated/requirements.txt -r /app/calibre-web-automated/optional-requirements.txt
115+
116+ # STEP 4 - Install kepubify
117+ RUN \
133118 echo "**** install kepubify ****" && \
134119 if [[ $KEPUBIFY_RELEASE == 'newest' ]]; then \
135120 KEPUBIFY_RELEASE=$(curl -sX GET "https://api.github.com/repos/pgaskin/kepubify/releases/latest" \
@@ -144,27 +129,13 @@ RUN \
144129 /usr/bin/kepubify -L \
145130 https://github.com/pgaskin/kepubify/releases/download/${KEPUBIFY_RELEASE}/kepubify-linux-arm64; \
146131 fi && \
147- # STEP 6 - Install Calibre
148- # STEP 6.1 - Install additional required packages
149- apt-get update && \
150- apt-get install -y --no-install-recommends \
151- libxtst6 \
152- libxrandr2 \
153- libxkbfile1 \
154- libxcomposite1 \
155- libopengl0 \
156- libnss3 \
157- libxkbcommon0 \
158- libegl1 \
159- libxdamage1 \
160- libgl1 \
161- libglx-mesa0 \
162- xz-utils \
163- binutils && \
164- # STEP 6.2 - Make the /app/calibre directory for the installed files
165- mkdir -p \
166- /app/calibre && \
167- # STEP 6.3 - Download the desired version of Calibre, determined by the CALIBRE_RELEASE variable and the architecture of the build environment
132+ chmod +x /usr/bin/kepubify
133+
134+ # STEP 5 - Install Calibre
135+ RUN \
136+ # STEP 5.1 - Make the /app/calibre directory for the installed files
137+ mkdir -p /app/calibre && \
138+ # STEP 5.2 - Download the desired version of Calibre, determined by the CALIBRE_RELEASE variable and the architecture of the build environment
168139 if [ "$(uname -m)" == "x86_64" ]; then \
169140 curl -o \
170141 /calibre.txz -L \
@@ -174,46 +145,149 @@ RUN \
174145 /calibre.txz -L \
175146 "https://download.calibre-ebook.com/${CALIBRE_RELEASE}/calibre-${CALIBRE_RELEASE}-arm64.txz" ; \
176147 fi && \
177- # STEP 6.4 - Extract the downloaded file to /app/calibre
148+ # STEP 5.3 - Extract the downloaded file to /app/calibre
178149 tar xf \
179150 /calibre.txz -C \
180151 /app/calibre && \
181- # STEP 6.4 .1 - Remove the ABI tag from the extracted libQt6* files to allow them to be used on older kernels
152+ # STEP 5.3 .1 - Remove the ABI tag from the extracted libQt6* files to allow them to be used on older kernels
182153 # Removed in V3.1.4 because it was breaking Calibre features that require Qt6. Replaced with a kernel check in the cwa-init service
183- # STEP 6.5 - Delete the extracted calibre.txz to save space in final image
184- rm /calibre.txz && \
185- # STEP 7 - ADD files referencing the versions of the installed main packages
186- echo "$VERSION" >| /app/CWA_RELEASE && \
187- echo "$KEPUBIFY_RELEASE" >| /app/KEPUBIFY_RELEASE && \
188- echo "$CALIBRE_RELEASE" > /CALIBRE_RELEASE
154+ # STEP 5.4 - Delete the extracted calibre.txz to save space in final image
155+ rm /calibre.txz
156+
157+ # ============================================================================
158+ # STAGE 2: Final - Build the final runtime image
159+ # ============================================================================
160+ FROM ghcr.io/linuxserver/baseimage-ubuntu:noble AS unrar-stage
161+ FROM ghcr.io/linuxserver/unrar:latest AS unrar
162+
163+ FROM ghcr.io/linuxserver/baseimage-ubuntu:noble
164+
165+ ARG BUILD_DATE
166+ ARG VERSION
167+ ARG CALIBREWEB_RELEASE=0.6.24
168+ ARG CALIBRE_RELEASE=8.9.0
169+ ARG KEPUBIFY_RELEASE=v4.0.4
170+
171+ LABEL build_version="Version:- ${VERSION}"
172+ LABEL build_date="${BUILD_DATE}"
173+ LABEL CW-base-version="${CALIBREWEB_RELEASE}"
174+ LABEL maintainer="CrocodileStick"
189175
190- # Removes packages that are no longer required, also emptying dirs used to build the image that are no longer needed
176+ # Set the default shell for the following RUN instructions to bash instead of sh
177+ SHELL ["/bin/bash" , "-c" ]
178+
179+ # Copy installed dependencies from the dependencies stage
180+ COPY --from=dependencies /lsiopy /lsiopy
181+ COPY --from=dependencies /usr/bin/kepubify /usr/bin/kepubify
182+ COPY --from=dependencies /app/calibre /app/calibre
183+ COPY --from=dependencies /usr/bin/lsof /usr/bin/lsof
184+ COPY --from=dependencies /usr/bin/python3.13 /usr/bin/python3.13
185+ COPY --from=dependencies /usr/lib/python3.13 /usr/lib/python3.13
186+
187+ # Install only runtime packages (no build tools)
191188RUN \
192- echo "**** cleanup ****" && \
193- apt-get -y purge \
194- build-essential \
195- libldap2-dev \
196- libsasl2-dev \
197- gettext \
198- python3.13-dev \
199- software-properties-common && \
189+ echo "**** add deadsnakes PPA for Python 3.13 runtime ****" && \
190+ apt-get update && \
191+ apt-get install -y --no-install-recommends software-properties-common && \
192+ add-apt-repository ppa:deadsnakes/ppa && \
193+ apt-get update && \
194+ echo "**** install runtime packages ****" && \
195+ apt-get install -y --no-install-recommends \
196+ imagemagick \
197+ ghostscript \
198+ libldap2 \
199+ libmagic1 \
200+ libsasl2-2 \
201+ libxi6 \
202+ libxslt1.1 \
203+ xdg-utils \
204+ inotify-tools \
205+ python3.13 \
206+ nano \
207+ sqlite3 \
208+ zip \
209+ libxtst6 \
210+ libxrandr2 \
211+ libxkbfile1 \
212+ libxcomposite1 \
213+ libopengl0 \
214+ libnss3 \
215+ libxkbcommon0 \
216+ libegl1 \
217+ libxdamage1 \
218+ libgl1 \
219+ libglx-mesa0 \
220+ xz-utils \
221+ curl && \
222+ # Create python3 symlink to point to python3.13
223+ ln -sf /usr/bin/python3.13 /usr/bin/python3 && \
224+ # Cleanup
225+ apt-get -y purge software-properties-common && \
200226 apt-get -y autoremove && \
201227 rm -rf \
202228 /tmp/* \
203229 /var/lib/apt/lists/* \
204230 /var/tmp/* \
205231 /root/.cache
206232
207- # add unrar
233+ # STEP 6 - Copy application files
234+ # Copy the rest of the application code (changes most frequently)
235+ COPY --chown=abc:abc . /app/calibre-web-automated/
236+
237+ # STEP 7 - Configure application
238+ RUN \
239+ # STEP 7.1 - Move contents of /app/calibre-web-automated/root to / and delete the /app/calibre-web-automated/root directory
240+ cp -R /app/calibre-web-automated/root/* / && \
241+ rm -R /app/calibre-web-automated/root/ && \
242+ # STEP 7.2 - Run CWA install script to make required dirs, set script permissions and add aliases for CLI commands ect.
243+ chmod +x /app/calibre-web-automated/scripts/setup-cwa.sh && \
244+ /app/calibre-web-automated/scripts/setup-cwa.sh && \
245+ # STEP 7.3 - Create koplugin.zip from KOReader plugin folder
246+ echo "~~~~ Creating koplugin.zip from KOReader plugin folder... ~~~~" && \
247+ if [ -d "/app/calibre-web-automated/koreader/plugins/cwasync.koplugin" ]; then \
248+ cd /app/calibre-web-automated/koreader/plugins && \
249+ # Calculate digest of all files in the plugin for debugging purposes
250+ echo "Calculating digest of plugin files..." && \
251+ PLUGIN_DIGEST=$(find cwasync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) && \
252+ echo "Plugin digest: $PLUGIN_DIGEST" && \
253+ # Create a file named after the digest inside the plugin folder
254+ echo "Plugin files digest: $PLUGIN_DIGEST" > cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
255+ echo "Build date: $(date)" >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
256+ echo "Files included:" >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
257+ find cwasync.koplugin -type f -name "*.lua" -o -name "*.json" | sort >> cwasync.koplugin/${PLUGIN_DIGEST}.digest && \
258+ zip -r koplugin.zip cwasync.koplugin/ && \
259+ echo "Created koplugin.zip from cwasync.koplugin folder with digest file: ${PLUGIN_DIGEST}.digest" ; \
260+ else \
261+ echo "Warning: cwasync.koplugin folder not found, skipping zip creation" ; \
262+ fi && \
263+ # STEP 7.4 - Move koplugin.zip to static directory
264+ if [ -f "/app/calibre-web-automated/koreader/plugins/koplugin.zip" ]; then \
265+ mkdir -p /app/calibre-web-automated/cps/static && \
266+ cp /app/calibre-web-automated/koreader/plugins/koplugin.zip /app/calibre-web-automated/cps/static/ && \
267+ echo "Moved koplugin.zip to static directory" ; \
268+ else \
269+ echo "Warning: koplugin.zip not found, skipping move to static directory" ; \
270+ fi && \
271+ # STEP 7.5 - ADD files referencing the versions of the installed main packages
272+ echo "$VERSION" >| /app/CWA_RELEASE && \
273+ echo "$KEPUBIFY_RELEASE" >| /app/KEPUBIFY_RELEASE && \
274+ echo "$CALIBRE_RELEASE" > /CALIBRE_RELEASE
275+
276+ # Add unrar from unrar stage
208277COPY --from=unrar /usr/bin/unrar-ubuntu /usr/bin/unrar
209278
210- # set calibre environment variable
279+ # Set calibre environment variable
211280ENV CALIBRE_CONFIG_DIR=/config/.config/calibre
212281
213- # ports and volumes
282+ # Ports and volumes
214283WORKDIR /config
215284# The default port CWA listens on. Can be overridden with the CWA_PORT_OVERRIDE environment variable.
216285EXPOSE 8083
217286VOLUME /config
218287VOLUME /cwa-book-ingest
219288VOLUME /calibre-library
289+
290+ # Health check for container orchestration
291+ # Uses shell form to support environment variable substitution for CWA_PORT_OVERRIDE
292+ HEALTHCHECK --interval=30s --timeout=3s --start-period=120s --retries=3 \
293+ CMD curl -f http://localhost:${CWA_PORT_OVERRIDE:-8083}/ || exit 1
0 commit comments