docker/Dockerfile
Rama main · réplica de lectura del repositorio original.
# syntax=docker/dockerfile:1.7
#
# Factorio headless server — multi-arch (linux/amd64 + linux/arm64).
#
# The download stage deliberately runs on the *build* platform ($BUILDPLATFORM)
# rather than the target platform. All it does is fetch and unpack a tarball, so
# running it natively avoids paying QEMU emulation cost on cross-builds; the
# target-arch binary is simply copied into the final stage, never executed here.
# trixie-slim (glibc 2.41), not bookworm-slim (glibc 2.36): Wube's arm64
# headless binary requires glibc >= 2.38 and fails to start on bookworm with
# "version `GLIBC_2.38' not found". Confirmed by booting the built image on
# both architectures — amd64 is unaffected by the newer base.
ARG DEBIAN_TAG=trixie-slim
# ---------------------------------------------------------------------------
# Stage 1: fetch + verify + unpack the official headless tarball
# ---------------------------------------------------------------------------
FROM --platform=${BUILDPLATFORM} debian:${DEBIAN_TAG} AS downloader
ARG TARGETARCH
ARG FACTORIO_VERSION
# Optional, per-architecture. Supplied separately rather than as a single value
# because one build-arg cannot be correct for every platform in a multi-arch
# manifest — and multi-arch is the normal case, so a single arg would mean the
# usual build never gets verified at all. Wube publishes sums at
# https://factorio.com/download/sha256sums/ (amd64 only at the time of writing),
# so a missing sum degrades to an unverified download rather than a hard failure.
ARG FACTORIO_SHA256_AMD64=""
ARG FACTORIO_SHA256_ARM64=""
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl xz-utils \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN set -eux; \
test -n "${FACTORIO_VERSION}" || { echo "FACTORIO_VERSION build-arg is required" >&2; exit 1; }; \
case "${TARGETARCH}" in \
amd64) distro=linux64; expected_sha="${FACTORIO_SHA256_AMD64}" ;; \
arm64) distro=linux-arm64; expected_sha="${FACTORIO_SHA256_ARM64}" ;; \
*) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
url="https://factorio.com/get-download/${FACTORIO_VERSION}/headless/${distro}"; \
echo "==> downloading ${url}"; \
curl -fSL --retry 5 --retry-delay 5 --retry-connrefused -o factorio.tar.xz "${url}"; \
if [ -n "${expected_sha}" ]; then \
echo "==> verifying sha256 for ${TARGETARCH}"; \
echo "${expected_sha} factorio.tar.xz" | sha256sum -c -; \
else \
echo "==> WARNING: no checksum supplied for ${TARGETARCH}, download is unverified"; \
fi; \
tar -xJf factorio.tar.xz -C /opt; \
rm -f factorio.tar.xz; \
# The upstream layout differs between architectures (bin/x64 on amd64,
# a different subdirectory on arm64), so resolve it once at build time and
# expose a stable path the entrypoint can rely on.
bin="$(find /opt/factorio/bin -maxdepth 2 -type f -name factorio | head -n1)"; \
test -n "${bin}" || { echo "could not locate the factorio binary in the tarball" >&2; exit 1; }; \
echo "==> binary found at ${bin}"; \
ln -sf "${bin}" /opt/factorio/factorio; \
# Keep the pristine example configs, drop the writable data dirs: those are
# created (and owned) at runtime on the mounted volume instead.
rm -rf /opt/factorio/saves /opt/factorio/mods /opt/factorio/config
# ---------------------------------------------------------------------------
# Stage 2: runtime
# ---------------------------------------------------------------------------
FROM debian:${DEBIAN_TAG} AS runtime
ARG PUID=845
ARG PGID=845
# curl - fetches mod portal metadata and mod zips for update-mods.sh
# jq - safe JSON generation for server-settings.json et al, and mod
# portal response parsing in update-mods.sh
# gosu - clean privilege drop when the container is started as root
# tini - PID 1 that reaps zombies and forwards SIGTERM for clean save-on-exit
# procps - provides pgrep, used by healthcheck.sh to find the server process
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gosu \
jq \
procps \
tini \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -g "${PGID}" factorio \
&& useradd -l -u "${PUID}" -g "${PGID}" -d /factorio -s /usr/sbin/nologin factorio
COPY --from=downloader /opt/factorio /opt/factorio
COPY entrypoint.sh healthcheck.sh update-mods.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/healthcheck.sh /usr/local/bin/update-mods.sh \
&& mkdir -p /factorio \
&& chown -R "${PUID}:${PGID}" /factorio /opt/factorio
VOLUME /factorio
WORKDIR /factorio
# 34197/udp — game traffic. 27015/tcp — RCON (only bound when a password is set)
EXPOSE 34197/udp 27015/tcp
ENV FACTORIO_DIR=/factorio \
FACTORIO_HOME=/opt/factorio \
PORT=34197 \
RCON_PORT=27015 \
SAVE_NAME=default \
GENERATE_NEW_SAVE=true \
UPDATE_CONFIG=true
HEALTHCHECK --interval=30s --timeout=5s --start-period=90s --retries=3 \
CMD /usr/local/bin/healthcheck.sh
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
# ---------------------------------------------------------------------------
# OCI labels (the build workflow overrides these via docker/metadata-action)
# ---------------------------------------------------------------------------
ARG FACTORIO_VERSION
LABEL org.opencontainers.image.title="factorio-server" \
org.opencontainers.image.description="Multi-arch Factorio headless server (amd64 + arm64)" \
org.opencontainers.image.version="${FACTORIO_VERSION}" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.vendor="Community" \
io.factorio.version="${FACTORIO_VERSION}"