Docker Bot Deployment: Reproducible Images, Persistent State and Secrets
TECH INSIGHT / EXPLAINED
Docker packages an application and its user-space dependencies into an image. It can reduce deployment differences, but the host kernel, CPU architecture, mounts, configuration and external services still matter.
Freeze an artifact, not just a tag
A tag such as python:3.12-slim can move to a newer image. For repeatable releases, choose a reviewed base digest, lock application dependencies and record the built image digest and target platform. Rebuilding the same Dockerfile later is not proof of identical bytes. Schedule reviewed updates so pinning does not mean never applying fixes.
This starter assumes a single-file Linux Python application plus requirements.txt. Replace the PYTHON_IMAGE build argument with the tested tag@sha256 digest for a release. Copying dependencies before source can reuse a cached install layer; the actual time saved depends on the build.
ARG PYTHON_IMAGE=python:3.12-slim
FROM ${PYTHON_IMAGE}
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN useradd --uid 10001 --create-home bot && mkdir /data && chown bot:bot /data
COPY --chown=bot:bot bot.py .
USER bot
CMD ["python", "-u", "bot.py"]Keep runtime state and credentials outside the image
Exclude sensitive files from the build context even when copying selected sources. Here is a starting .dockerignore; adapt it to every credential and output path in your project.
.git
.env*
secrets/
config*.ini
data/
logs/
__pycache__/
.venv/Make the application contract visible in Compose
The example starts without automatic restart while deployment behavior is being tested. BOT_MODE, DATA_DIR and the *_FILE names are application conventions: bot.py must implement them, fail on invalid settings and actually disable order submission in paper mode. Docker does not infer those meanings. Create protected local secret files; do not commit them.
services:
trading-bot:
build: .
init: true
restart: "no"
environment:
BOT_MODE: paper
DATA_DIR: /data
API_KEY_FILE: /run/secrets/api_key
API_SECRET_FILE: /run/secrets/api_secret
secrets:
- api_key
- api_secret
volumes:
- bot_data:/data
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
secrets:
api_key:
file: ./secrets/api_key.txt
api_secret:
file: ./secrets/api_secret.txt
volumes:
bot_data: {}Understand what survives
The writable layer survives a stop/start of the same container and is lost when that container is removed. A named volume survives replacement unless explicitly removed; it still needs backup and restore tests. Moving only compose.yaml does not migrate database files, secrets or registry access.
Compose mounts the granted secrets as files. This avoids baking credentials into an image, but does not encrypt the source files or protect them from an administrator controlling the host. Verify that the non-root application can read the secret mounts and write its data volume without making them publicly writable.
Test startup, shutdown and recovery
In a Docker-enabled test environment, run docker compose config --quiet, build the image, then verify startup with dummy credentials and a paper adapter. Test persistence after container replacement, graceful SIGTERM handling and restart reconciliation. A health status alone does not make Docker restart a hung process. After recovery is proven, an appropriate restart policy such as unless-stopped can be selected with its documented manual-stop behavior.
Keep time handling explicit in application code; setting TZ without the required timezone data is insufficient. Unbuffered Python output helps timely logs, while rotation bounds disk use. A Linux container cannot simply run a Windows COM dependency. The configuration here was syntax-checked; no container runtime build is claimed.
Sources and further reading
Docker build practices · Docker persistence · Compose secrets · Restart policies · Python timezone data
Content and examples reviewed: 2026-09-29
댓글
댓글 쓰기