Português: DEPLOY.pt_BR.md
You can run the application with plain Docker (docker run), Docker Compose, Docker Swarm, or Kubernetes — choose the option that fits your environment. All options use the same image; default behaviour is web API + frontend on port 8088.
When you run the image without overriding the command (e.g. docker run ... image, Compose, Swarm, or Kubernetes with no command/args), the container starts the web API and frontend:
- Dashboard:
http://<host>:8088/(status, Start scan button, recent sessions, download reports). The Start scan button runs a full audit of all targets defined in/data/config.yaml(same as the CLI one-shot with that config). - Reports:
http://<host>:8088/reports - Configuration:
http://<host>:8088/config - API docs:
http://<host>:8088/docs - Health:
http://<host>:8088/health(imageHEALTHCHECKprobes this URL on loopback in JSON exec form — distroless has no shell)
Port 8088 is exposed. Config and persistent data (SQLite, reports) live under /data (mount a volume or bind mount with config.yaml).
To run a single audit from the CLI instead of the API, override the container command and pass config (and optional --tenant / --technician):
# Bind mount a dir that contains config.yaml at /data
docker run --rm -v "$(pwd)/data:/data" \
YOUR_IMAGE \
python main.py --config /data/config.yaml --tenant "Acme" --technician "Ops"Or with --entrypoint:
docker run --rm -v "$(pwd)/data:/data" \
--entrypoint python \
YOUR_IMAGE \
main.py --config /data/config.yamlThe report is written under report.output_dir in config (e.g. /data); copy it out from the volume or bind-mounted dir. Do not use --web in these overrides.
You can run the application without cloning the repository by using the published image on Docker Hub:
- Docker Hub: hub.docker.com/r/fabioleitao/data_boar —
fabioleitao/data_boar:latestandfabioleitao/data_boar:1.7.4
Example:
docker pull fabioleitao/data_boar:latest
docker run -d -p 8088:8088 -v "$(pwd)/data:/data" -e CONFIG_PATH=/data/config.yaml fabioleitao/data_boar:latestEnsure /data/config.yaml exists (e.g. copy from deploy/config.example.yaml in the repo or create a minimal config). This lets users choose to run an instanced container from Docker Hub instead of building from Git.
Upgrading when new versions are pushed: To refresh your local image so it matches the current version on Docker Hub, run docker pull fabioleitao/data_boar:latest, then restart your container or stack (e.g. docker stop/docker rm and docker run again, or docker compose pull and docker compose up -d). Do this whenever you want to pick up the latest image from the repository.
- Dockerfile at repo root. Build (Data Boar branding):
docker build -t fabioleitao/data_boar:latest .or a local tag:docker build -t data_boar:latest . - Public image: You can also push to GitHub Container Registry (ghcr.io) or your own Docker Hub account and use that name in Compose/Swarm/Kubernetes.
Image footprint: The Dockerfile uses a multi-stage build on python:3.13-slim (see repo Dockerfile; aligns with requires-python >=3.12 and CI on 3.12/3.13). The first stage installs build tools (gcc, dev headers) and compiles Python extensions; the final stage copies only the installed packages and app code and installs runtime libraries (e.g. libpq5, libffi8, unixodbc, libmariadb3). Build tools and -dev packages are not included in the final image, reducing size and attack surface while keeping the same behaviour (all DB connectors, reports, API).
# From repo root (Data Boar image name)
docker build -t ghcr.io/fabioleitao/data_boar:latest .
docker login ghcr.io # Use a GitHub Personal Access Token with read:packages, write:packages
docker push ghcr.io/fabioleitao/data_boar:latest# From repo root
docker build -t fabioleitao/data_boar:latest -t fabioleitao/data_boar:1.7.4 .
docker login
# Username: fabioleitao (or your Docker Hub username)
# Password: your Docker Hub password or Access Token
docker push fabioleitao/data_boar:latest
docker push fabioleitao/data_boar:1.7.4Then in deploy/docker-compose.yml set image: to your pushed image (e.g. fabioleitao/data_boar:latest or ghcr.io/fabioleitao/...).
Releasing to Docker Hub (fabioleitao): From repo root, run the test suite (uv run pytest -v -W error), then build, log in with your Docker Hub credentials, and push the branded image:
uv run pytest -v -W error
docker build -t fabioleitao/data_boar:latest -t fabioleitao/data_boar:1.7.4 .
docker login # username: fabioleitao, password: your token
docker push fabioleitao/data_boar:latest
docker push fabioleitao/data_boar:1.7.4See also DOCKER_SETUP.md.
The app expects config at /data/config.yaml inside the container. Use the same schema as in the repo (see USAGE.md). Minimum for API-only:
targets: []report.output_dir: /datasqlite_path: /data/audit_results.dbapi.port: 8088
Sensitivity detection (ML/DL): You can configure training terms for pattern and sensitivity detection via ml_patterns_file, dl_patterns_file, or inline sensitivity_detection.ml_terms / sensitivity_detection.dl_terms in config. Mount your terms file(s) under /data (e.g. /data/ml_terms.yaml) and set the paths in config. See SENSITIVITY_DETECTION.md and deploy/config.example.yaml for examples. The image includes regex + ML (TF-IDF + RandomForest); optional DL (sentence embeddings) requires installing the .[dl] extra when building a custom image.
Copy deploy/config.example.yaml and edit, then either:
- Volume: Create a volume and put
config.yamlinto it before first run, or - Bind mount: Mount a host dir that contains
config.yamlat/data.
Example with bind mount (host dir ./data):
mkdir -p data
cp deploy/config.example.yaml data/config.yaml
# Edit data/config.yaml (targets, etc.)In production the main bottlenecks are network I/O (database and API targets) and filesystem I/O (file scans, SQLite, report generation). The default resource settings balance minimal footprint with enough headroom for these I/O-bound operations.
- Memory: Reservations/requests of 256Mi and limits of 1Gi are default. Use at least 256Mi so the process does not get OOM-killed; 1Gi allows report generation (Excel, heatmaps) and concurrent scans. Increase limits if you run many parallel targets or large reports.
- CPU: A small request (e.g. 0.25 in Compose, 100m in Kubernetes) helps the scheduler place the container/pod; CPU limits are left unset or high so short CPU bursts (e.g. report generation) are not throttled. The workload is I/O-bound, so CPU is not the bottleneck.
- api.workers: Default 1 keeps footprint minimal. Set 2 (or more) in config if you need to serve many concurrent API requests while scans run; each worker is a separate process.
- scan.max_workers: Default 1 (sequential targets). Set 2–4 when you have many targets and want parallel scan threads; I/O (DB/FS) remains the bottleneck, so this improves throughput without requiring much more CPU.
- Storage for /data: Use fast storage (SSD, local NVMe) for the volume that holds SQLite and report output; this reduces write latency and speeds up report generation and dashboard reads.
- Timeouts: REST/API connectors use configurable timeouts (see USAGE.md). Increase them in target config if your network or APIs are slow to avoid false timeouts.
The following are optional practices to harden deployments. They do not replace securing the app at the reverse proxy (TLS, auth, WAF). See also SECURITY.md.
- Non-root: The image already runs as distroless nonroot (UID/GID 65532). To enforce at runtime:
docker run --user 65532:65532 ...(matchesUSER 65532:65532in the Dockerfile). There is noappuseraccount name in the image (/etc/passwdis not populated with that name). - Resource limits: Use
--cpusand--memoryfor plain Docker (e.g.--memory 1g). In Compose, setdeploy.resources.limits(e.g.cpus: '1',memory: 1G). - Healthchecks: The image includes a Docker
HEALTHCHECKthat probesGET /healthon loopback via stdliburllib(JSON exec form — distroless has no shell). Compose uses the same exec probe. Kubernetes examples in this repo still declarereadinessProbe/livenessProbeseparately. For observability, SLO/SLI/SLA, and SRE alignment (runbooks, error budgets, optional metrics), see OBSERVABILITY_SRE.md. - DevSecOps: Combine with API key (
api.require_api_key), rate limiting (rate_limitin config), and CSP/security headers (see SECURITY.md). Run behind a reverse proxy with TLS and, when exposed externally, consider a WAF. For production, setapi.require_api_key: trueand use a strong key from an environment variable (e.g.api.api_key_from_env: "AUDIT_API_KEY") so credentials are not stored in the config file.
You can tighten the Deployment with a securityContext and add a NetworkPolicy and PodDisruptionBudget for production. The base manifests in deploy/kubernetes/ do not include these by default; below are optional snippets and example files.
- Deployment securityContext (add under
spec.template.specin your Deployment):
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: data-boar
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# ... rest of container spec; ensure /data is a writable volumeMount so the app can write SQLite and reportsThe image runs as UID/GID 65532 (Google distroless :nonroot). Use a writable volume for /data (e.g. PVC or emptyDir) so SQLite and report output work with readOnlyRootFilesystem: true.
-
NetworkPolicy: To restrict ingress to the API (e.g. only from an ingress controller or a specific namespace), use an example like
deploy/kubernetes/network-policy.example.yaml. Apply it only if your cluster supports NetworkPolicy. -
PodDisruptionBudget (PDB): When running multiple replicas, a PDB can ensure at least one replica remains during voluntary disruptions. See
deploy/kubernetes/pdb.example.yaml. Apply only if you run more than one replica. -
Resource requests/limits: The default Deployment already sets memory request/limit and CPU request; see "Resource and I/O tuning" above for guidance.
# From repo root; image built locally (Data Boar)
docker build -t data_boar:latest .
# Run with a bind mount for /data (config.yaml must be in ./data)
mkdir -p data
cp deploy/config.example.yaml data/config.yaml
# Edit data/config.yaml as needed
# Bind-mount ownership must match the image user (distroless nonroot):
# sudo chown -R 65532:65532 data
# Named volumes (see Compose below) are usually initialized with the correct UID.
docker run -d --name data-boar-audit \
-p 8088:8088 \
-v "$(pwd)/data:/data" \
-e CONFIG_PATH=/data/config.yaml \
data_boar:latestAccess: http://localhost:8088/ (dashboard), http://localhost:8088/docs (API). In Docker and Kubernetes examples, the process runs as UID 65532; if a bind-mounted /data is owned by UID 1000 (or root-only), you may see misleading failures such as “config not found” or “unable to open database file” that are really permission errors. Prefer a named volume (Compose default) or chown 65532:65532 on the host path. The service is exposed via container/Service port bindings, so it is safe to keep the internal API binding on 0.0.0.0 inside the container while still using 127.0.0.1 as the default when running the CLI directly on a workstation. To stop: docker stop data-boar-audit && docker rm data-boar-audit.
Docker Compose is a simple alternative to Swarm for single-host or small setups. Use the Compose file in deploy/ and optionally an override to bind-mount your config.
From the repository root:
mkdir -p data
cp deploy/config.example.yaml data/config.yaml
# Edit data/config.yaml (targets, report.output_dir, etc.)By default deploy/docker-compose.yml uses a named volume data-boar-data. To use a host directory (e.g. ./data) instead, use the override file:
cp deploy/docker-compose.override.example.yml deploy/docker-compose.override.yml
# deploy/docker-compose.override.yml already sets volumes: - ./data:/dataIf you use the named volume, ensure config.yaml is placed inside it (e.g. via a one-off container) before the first run.
# From repo root
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.override.yml up -dIf you did not create an override, use:
docker compose -f deploy/docker-compose.yml up -d(Then populate the data-boar-data volume with config.yaml if needed.)
- Dashboard: http://localhost:8088/
- API docs: http://localhost:8088/docs
- Health: http://localhost:8088/health
# View logs
docker compose -f deploy/docker-compose.yml logs -f data-boar
# Stop and remove
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.override.yml downIn deploy/docker-compose.yml set image: fabioleitao/data_boar:latest (or your registry) and remove or comment out the build: block. Then run the same docker compose ... up -d commands.
Podman is a daemonless, rootless container runtime — the default on RHEL/Fedora and recommended for zero-trust environments.
podman run -d --name data-boar \
-p 8088:8088 \
-v ./data:/data:z \
fabioleitao/data_boar:latestSELinux note: Use
:z(shared label) or:Z(private label) on the volume mount when SELinux is enforcing. Omit the label on non-SELinux hosts.
For Compose-style workflows, podman-compose is compatible with the
existing deploy/docker-compose.yml:
pipx install podman-compose
podman-compose -f deploy/docker-compose.yml up -dDocker Swarm is an alternative for orchestrating the service across a cluster. Use the same Compose file with docker stack deploy.
docker swarm initFrom the repository root:
# Ensure config is available to the service. Option 1: bind mount a host dir
mkdir -p data
cp deploy/config.example.yaml data/config.yaml
# Edit data/config.yaml
# Override volume to bind mount ./data into the service (create override from the example first)
cp deploy/docker-compose.override.example.yml deploy/docker-compose.override.yml
docker stack deploy -c deploy/docker-compose.yml -c deploy/docker-compose.override.yml data-boar-auditIf you use the default named volume data-boar-data, the first time the service starts the container will see an empty /data. You must put config.yaml into that volume (e.g. run a one-off container that mounts the volume and copies a file in, or use a config mount). Easiest for local: bind mount.
Create deploy/docker-compose.override.yml (or a fragment) to bind mount without editing the main file:
# deploy/docker-compose.override.yml (optional, for bind mount)
version: "3.8"
services:
data-boar:
volumes:
- ./data:/data
Then from repo root:
docker stack deploy -c deploy/docker-compose.yml -c deploy/docker-compose.override.yml data-boar-auditIf you don't use an override, ensure the stack's data-boar-data volume is populated with config.yaml (e.g. via a one-off container).
docker service ls
docker service ps data-boar-audit_data-boar
docker service logs data-boar-audit_data-boar- Dashboard: http://localhost:8088/
- API: http://localhost:8088/docs
Port 8088 is exposed by the Compose file.
docker stack rm data-boar-auditKubernetes is an alternative to Docker Swarm for running the app in a cluster. The same image runs with default command (web API + frontend). Manifests are in deploy/kubernetes/.
- kubectl and access to a cluster.
- Image: Set the image in
deploy/kubernetes/deployment.yaml(e.g.fabioleitao/data_boar:latestor your registry). - Config: The included ConfigMap provides a minimal
config.yaml. Replace or extend it for production (seedeploy/kubernetes/README.md).
From the repository root:
# Optional: set image via env (or edit deploy/kubernetes/deployment.yaml)
export IMAGE=fabioleitao/data_boar:latest
kubectl apply -f deploy/kubernetes/This creates the Deployment, Service (ClusterIP on 8088), and ConfigMap. For full details (NodePort, LoadBalancer, Ingress, persistence) see deploy/kubernetes/README.md.
Expose the service via NodePort, LoadBalancer, or Ingress to port 8088. Then:
- Dashboard: <http://<external>:8088/>
- API docs: <http://<external>:8088/docs>
- Health: <http://<external>:8088/health>
To run a single audit from the CLI in the cluster, use a Job that overrides the container command. Example and details are in deploy/kubernetes/README.md.
- In
deploy/docker-compose.ymlsetimage:to your published image (e.g.fabioleitao/data_boar:latest) and remove or comment out thebuild:block. - Prepare
/data/config.yamlas in section 2 (volume or bind mount). - Run with docker run (section 3), Docker Compose (section 4), Docker Swarm (section 5), or Kubernetes (section 6) as above.
Why this matters: Public tags remain pullable until you delete them on Docker Hub. Cleaning up obsolete tags reduces casual reuse of old builds (CVEs, wrong defaults) and aligns docs with what you actually support.
- Inventory: In Docker Hub list all tags; note which CI, partners, or docs pin each tag (e.g.
latest,1.6.5). - Decide support policy: Typically keep
latestplus current semver (and optionally one previous semver for rollback). Document the supported set here; maintainers may also mirror the matrix under Internal and reference in README.md or SECURITY.md when useful. - Delete on Hub: Hub → repository → Tags → delete tags you no longer support. Warning: Anyone who already pulled a tag still has that image locally; deletion only stops new pulls from Hub.
- Update automation: Adjust CI/CD and
docker-compose/ Kubernetes manifests so nothing references removed tags. - Commercial / IP note: Tag hygiene is not a substitute for licensing or private issuer repos; it complements them. See CODE_PROTECTION_OPERATOR_PLAYBOOK.md.
Português (Brasil): DEPLOY.pt_BR.md §8.
Typical deployments keep all durable state under /data (volume or bind mount): config.yaml, the SQLite file from sqlite_path (default /data/audit_results.db), and report output under report.output_dir (often /data or a subdirectory). Optional files such as ML/DL term YAMLs belong in the same tree if referenced by config.
- Copy or snapshot the entire host directory or volume you mount at
/data(or the Kubernetes PVC contents), so you preserve config, database, reports, and any auxiliary files paths inconfig.yamlpoint to.
- Stop the container, Compose stack, Swarm service, or scale the Kubernetes Deployment to zero.
- Restore files onto a new volume or directory at the same mount path (
/datainside the container). - Ensure file ownership is compatible with the image user (UID/GID 65532, distroless nonroot) when using bind mounts on Linux hosts.
- Start the workload again; verify
GET /health, then run a short scan or open the dashboard to confirm SQLite and reports are visible.
- Prefer API keys from environment (
api.api_key_from_env) so secrets are not only in files you include in file-level backups—see SECURITY.md. - For long-term recovery, record the image tag or digest you ran (e.g.
fabioleitao/data_boar:1.7.4) alongside the data backup so you can match behaviour when restoring.
Português (Brasil): DEPLOY.pt_BR.md §9.
Podman is an alternative container runtime to Docker with a similar CLI. It runs rootless by default, which means containers do not require root privileges on the host. This section covers the basic deployment pattern; it does not document lab-specific private configurations.
| Topic | Docker | Podman |
|---|---|---|
| Default mode | Root (unless rootless configured) | Rootless (per-user) |
| Daemon | Background daemon required | Daemonless (fork/exec) |
| Compose | Docker Compose v2 (docker compose) |
podman-compose or podman kube play |
| Socket path | /var/run/docker.sock |
/run/user/<UID>/podman/podman.sock |
| Image compat | OCI images (same format) | OCI images (compatible) |
# Pull the image from Docker Hub (same image as Docker deployments)
podman pull fabioleitao/data_boar:latest
# Run as API + frontend (port 8088) with a bind mount for persistent data
podman run -d \
--name data-boar \
-p 8088:8088 \
-v ./data:/data:Z \
-e CONFIG_PATH=/data/config.yaml \
fabioleitao/data_boar:latestNote: The
:Zsuffix on the volume mount sets the SELinux label for rootless Podman on Fedora/RHEL-based systems. On Debian/LMDE it is ignored safely.
podman run --rm \
-v ./data:/data:Z \
fabioleitao/data_boar:latest \
python main.py --config /data/config.yaml --output /data/report.xlsx# Check status
podman ps
# View logs
podman logs -f data-boar
# Stop and remove
podman stop data-boar && podman rm data-boarOn systems with systemd (LMDE, Debian, Fedora), you can generate a systemd unit from the running container so it starts automatically on login:
podman generate systemd --name data-boar --files --new
mkdir -p ~/.config/systemd/user
mv container-data-boar.service ~/.config/systemd/user/
systemctl --user enable --now container-data-boar.servicepodman-compose accepts the same docker-compose.yml format:
pip install podman-compose # or use your distro package
podman-compose -f deploy/docker-compose.yml up -dOperational lessons from lab deployments (LAB-NODE-02, LMDE 7) will be documented in docs/ops/LAB_LESSONS_LEARNED.md as they accumulate. Check that file for host-specific troubleshooting tips.
Português (Brasil): DEPLOY.pt_BR.md §10.
| Goal | Command / step |
|---|---|
| Default (API + frontend) | Run image with no command override: docker run, Compose, Swarm, or Kubernetes |
| CLI one-shot | Override command: docker run ... --entrypoint python IMAGE main.py --config /data/config.yaml |
| Build image | docker build -t data_boar:latest . or docker build -t fabioleitao/data_boar:latest . |
| Push to registry | docker tag ... fabioleitao/data_boar:latest then docker login and docker push fabioleitao/data_boar:latest |
| Single container | docker run -d -p 8088:8088 -v ./data:/data data_boar:latest (section 3) |
| Docker Compose | docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.override.yml up -d — prepare ./data/config.yaml first (section 4) |
| Docker Swarm | docker stack deploy -c deploy/docker-compose.yml -c deploy/docker-compose.override.yml data-boar-audit (section 5) |
| Kubernetes | kubectl apply -f deploy/kubernetes/ — see deploy/kubernetes/README.md for image and config (section 6) |
| Backup / restore | Persisted data under /data (section 9): back up the mounted volume or bind mount; restore with the same layout, then verify /health |
| Podman (rootless) | podman pull fabioleitao/data_boar:latest then podman run -d -p 8088:8088 -v ./data:/data:Z ... (section 10) |
You can use Docker Compose or Kubernetes as alternatives to Docker Swarm; same image and config layout apply. All paths and image names assume you are in the repo root or in deploy/ as indicated.
The application runs correctly when placed behind NAT, a load balancer, or a reverse proxy (nginx, Traefik, Caddy, or similar). No code or config change is required for basic operation.
- TLS at the proxy: If HTTPS is terminated at the proxy (recommended), set X-Forwarded-Proto: https on requests to the app so that security headers (e.g. HSTS) and scheme detection work correctly. Also set
api.trusted_proxy_cidrsto the CIDR(s) of the direct proxy peer; otherwise forwarded headers are ignored and the dashboard may still show the plaintext-HTTP risk banner. With a trusted peer +https,GET /statusexposeseffective_external_transport(tls_termination: trusted_proxy) while process-leveldashboard_transportremains HTTP. See SECURITY.md and SECURE_DASHBOARD_AUTH_AND_HTTPS_HOWTO.md §B.1. - Client IP and host: If you need the real client IP or original host in logs or logic, configure your proxy to send X-Forwarded-For and X-Forwarded-Host; the app can be extended to trust these when needed.
- Subpath: If the app is served under a path prefix (e.g. https://example.com/audit/), configure the proxy to strip or rewrite the prefix so the app still sees paths starting at
/; or use the proxy's rewrite rules to map/audit/to the container root.
Documentation index (all topics, both languages): ../README.md · ../README.pt_BR.md. Technical guide: ../TECH_GUIDE.md · ../TECH_GUIDE.pt_BR.md.