File size: 6,628 Bytes
6c3af4e
 
a39b7f4
 
e3a5245
6c3af4e
 
 
 
 
 
 
 
1a6d0d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b5cd13d
 
1a6d0d2
b5cd13d
1a6d0d2
 
 
6c3af4e
 
 
1a6d0d2
 
6c3af4e
1a6d0d2
6c3af4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a6d0d2
 
 
4b4ae63
 
1a6d0d2
 
 
 
 
 
 
 
 
4b4ae63
 
 
 
 
 
 
 
 
1a6d0d2
 
 
 
6c3af4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a6d0d2
 
 
 
 
6c3af4e
 
 
1a6d0d2
 
6c3af4e
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
FROM node:lts-slim

# Renovate keeps this pin current via the customManager in renovate.json,
# whose regex matches this exact line: keep the quoted 40-character form.
ARG SEARXNG_COMMIT_SHA="d48c4b555421e824342c51d68482dd0898e54d0f"

ENV PORT=7860
EXPOSE $PORT

ARG USERNAME=node
ARG HOME_DIR=/home/${USERNAME}
ARG APP_DIR=${HOME_DIR}/app

# The slim base ships `npm`'s dependency tree at stale releases, and npm
# pins that tree exactly, so neither `npm install -g npm@latest` nor a
# clean-prefix install lifts the bundled copies off flagged versions.
# The patched releases are installed into a scratch project here, and
# scripts/npm-bundle-overlay.cjs copies them over every stale copy under
# /usr/local/lib/node_modules after verifying every declaration in the
# tree accepts them; it then re-walks the tree and fails the build if any
# flagged version survives. The scratch install is outside the app lockfile,
# so these pins are the only place the versions live. npm itself is pinned:
# an unpinned `npm@latest` would let an upstream release break this build
# at any moment.
COPY scripts/npm-bundle-overlay.cjs /tmp/npm-bundle-overlay.cjs

RUN npm install --global npm@12.0.2 && \
  mkdir -p /tmp/overlay && \
  printf '{"name":"overlay","private":true}' > /tmp/overlay/package.json && \
  npm install --prefix /tmp/overlay --no-audit --no-fund --ignore-scripts \
    tar@7.5.22 \
    brace-expansion@5.0.12 \
    ip-address@10.7.2 \
    undici@6.28.1 \
    postcss-selector-parser@7.1.6 && \
  node /tmp/npm-bundle-overlay.cjs && \
  node -e "const r=require, p='/usr/local/lib/node_modules/npm/node_modules/'; for (const m of ['tar','brace-expansion','ip-address','undici','postcss-selector-parser']) r(p+m); console.log('overlay require smoke ok')" && \
  npm cache clean --force && \
  rm -rf /root/.npm /tmp/overlay /tmp/npm-bundle-overlay.cjs

# The slim base omits tools the full `node` image ships implicitly: `git` for
# the SearXNG checkout and the build's commit hash, `curl` for the HEALTHCHECK
# below, `openssl` for the SearXNG secret key, and `ca-certificates` for both
# the clone and pip. `apt-get upgrade` pulls the patched Debian releases of
# libraries the base pins (libpcre2, liblzma5, ...).
RUN apt-get update && \
  apt-get upgrade -y && \
  apt-get install -y --no-install-recommends \
  ca-certificates \
  curl \
  git \
  openssl \
  python3 \
  python3-venv && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/*

RUN mkdir -p /usr/local/searxng /etc/searxng && \
  chown -R ${USERNAME}:${USERNAME} /usr/local/searxng /etc/searxng && \
  chmod 755 /etc/searxng

WORKDIR /usr/local/searxng
RUN python3 -m venv searxng-venv && \
  chown -R ${USERNAME}:${USERNAME} /usr/local/searxng/searxng-venv && \
  /usr/local/searxng/searxng-venv/bin/pip install --upgrade pip && \
  /usr/local/searxng/searxng-venv/bin/pip install wheel setuptools pyyaml lxml

RUN git clone https://github.com/searxng/searxng.git /usr/local/searxng/searxng-src && \
  git -C /usr/local/searxng/searxng-src checkout $SEARXNG_COMMIT_SHA && \
  chown -R ${USERNAME}:${USERNAME} /usr/local/searxng/searxng-src

ARG SEARXNG_SETTINGS_PATH="/etc/searxng/settings.yml"

COPY --chown=${USERNAME}:${USERNAME} searxng-settings.yml $SEARXNG_SETTINGS_PATH

WORKDIR /usr/local/searxng/searxng-src
RUN chmod 644 $SEARXNG_SETTINGS_PATH && \
  sed -i 's/ultrasecretkey/'$(openssl rand -hex 32)'/g' $SEARXNG_SETTINGS_PATH && \
  /usr/local/searxng/searxng-venv/bin/pip install -r requirements.txt && \
  /usr/local/searxng/searxng-venv/bin/pip install --no-build-isolation -e . && \
  /usr/local/searxng/searxng-venv/bin/pip uninstall -y wheel setuptools && \
  /usr/local/searxng/searxng-venv/bin/pip uninstall -y pip && \
  /usr/local/searxng/searxng-venv/bin/python -c "import searx.webapp" && \
  rm -f /tmp/sxng_cache_*

# The runtime never pip-installs, and pip itself vendors flagged copies of
# msgpack and setuptools (`pip/_vendor/vendor.txt`) that no released pip
# has bumped, so the pip tree is removed from the shipped venv. SearXNG
# needs neither pip nor setuptools at runtime: a grep of `searx/` for
# `pkg_resources`/`import pip` at the pinned commit comes up empty, and
# the `import searx.webapp` build check above proves the package imports
# after the removal. The editable install finder is plain importlib.

# That same import also creates SearXNG's SQLite caches in the temp directory,
# owned by root because the build runs as root. The container runs as `node`,
# and SearXNG wipes and rebuilds those caches whenever `server.secret_key`
# differs from the one written above, so shipping them makes an instance with
# its own key die at startup with `attempt to write a readonly database`, with
# no search and a container that still reports healthy (#2732). Clearing them
# lets the running user create its own on first start; the `-shm` and `-wal`
# sidecars go with them, or SQLite fails on those instead.

# Create the app directory while still root and hand it to the app user:
# the legacy (non-BuildKit) builder creates WORKDIR directories as root even
# under USER, which then breaks `npm ci`'s mkdir of node_modules.
RUN mkdir -p ${APP_DIR} && chown ${USERNAME}:${USERNAME} ${APP_DIR}

USER ${USERNAME}

WORKDIR ${APP_DIR}

COPY --chown=${USERNAME}:${USERNAME} ./package.json ./package-lock.json ./.npmrc ./

RUN npm ci

COPY --chown=${USERNAME}:${USERNAME} . .

# The commit hash is optional build metadata, so a build context without a
# usable repository must not fail the build. This happens when building from a
# git worktree, where `.git` is a file pointing at a gitdir outside the context;
# git then treats every command as fatal, including `config --global`.
# Dev-only packages (the native TypeScript compiler, Playwright, Vitest, ...)
# are build-time tools the running server never loads; pruning them keeps
# advisory surface out of the shipped image. `vite` and the plugins it loads
# at preview time stay because `npm start` runs `vite preview` and the server
# hooks load with the config.
RUN git config --global --add safe.directory ${APP_DIR} 2>/dev/null || true; \
  git rev-parse --short HEAD >/dev/null 2>&1 || \
  echo "WARNING: no usable git repository in the build context, so the app will report an empty commit hash."; \
  npm run build && \
  npm prune --omit=dev

HEALTHCHECK --start-period=60s --interval=30s --timeout=10s --retries=3 CMD curl -fsS http://localhost:${PORT}/status || exit 1

ENTRYPOINT [ "/bin/sh", "-c" ]

CMD ["(cd /usr/local/searxng/searxng-src && /usr/local/searxng/searxng-venv/bin/python -m searx.webapp > /dev/null 2>&1) & npm start -- --host"]