Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ FORAIL_NODE_TYPE=hybrid
#FORAIL_TASK_CGROUP=host

# ── Docker images ────────────────────────────────────────────────────────────
# Pin to a release tag in production (e.g., 2026.04.0), not latest.
# Pin to a release tag in production, not latest. The tag has to exist for
# BOTH images -- 2026.07.1 was published for the backend only, so pinning it
# leaves the frontend unresolvable.
#
# Do not go below 2026.07.2-rc1 for a single-node install: 2026.07.0 registers
# the node as `control` and makes the `default` queue a Kubernetes container
# group, so every job sits in `pending` forever on Compose.
FORAIL_BACKEND_IMAGE=ghcr.io/forail-platform/forail-backend
FORAIL_FRONTEND_IMAGE=ghcr.io/forail-platform/forail-frontend
FORAIL_TAG=2026.07.0
FORAIL_TAG=2026.07.2-rc1

# Subject name for the self-signed certificate generated on first start when
# nginx/ssl/ is empty. Ignored once a real certificate is in place.
FORAIL_TLS_CN=localhost

# ── Nginx / Networking ──────────────────────────────────────────────────────
#NGINX_HTTP_PORT=80
Expand Down
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ cd forail-devops

# 2. Configure environment
cp .env.example .env
# Edit .env with real values
# Set the four `changeme` values in .env -- the stack will not come up without
# them. The rest has working defaults.

# 3. Start
docker compose up -d
Expand All @@ -33,6 +34,29 @@ docker compose up -d
docker compose ps # every service should report (healthy)
```

The UI is then on <https://localhost>; `http://` redirects to it. Log in with
`FORAIL_ADMIN_USER` / `FORAIL_ADMIN_PASSWORD` from your `.env`.

On first start, when `nginx/ssl/` is empty, a **self-signed** certificate is
generated so nginx has something to serve — your browser will warn about it.
Drop a real certificate into `nginx/ssl/` and it is left alone; see
[README-prod.md](README-prod.md).

### Running jobs

The stack above serves the API and the UI, but it **cannot execute anything** —
projects will not sync and every job stays in `pending`. Job execution runs
playbooks through podman inside the task container, which needs cgroup, mount
and user-namespace privileges:

```bash
FORAIL_TASK_PRIVILEGED=true FORAIL_TASK_CGROUP=host docker compose up -d
```

This is **off by default on purpose**: a privileged container is a trivial
escape to host root. Turn it on only on a host you are willing to treat as a
dedicated job runner. This is tracked as H4 in the security notes.

The scripts under `scripts/` are the container health probes — Compose mounts
them into the containers and runs them there (`bash /etc/forail/healthcheck-web.sh`),
so they call `forail-manage` and only work inside the image. Run one by hand with:
Expand Down
29 changes: 29 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,33 @@ services:
restart: unless-stopped

# ── External nginx (TLS termination + routing) ─────────
# ── TLS certificate bootstrap ─────────────────────────
# nginx terminates TLS and refuses to start without a certificate, so a
# clean `docker compose up -d` used to leave it in a restart loop with
# nothing serving on 80 or 443. Generate a self-signed pair when none is
# present; an operator-supplied certificate in nginx/ssl is never touched.
# Self-signed is for getting started only -- see README-prod.md for the
# real thing.
nginx-cert-init:
image: alpine:3.20
command:
- sh
- -c
- |
if [ -f /ssl/fullchain.pem ] && [ -f /ssl/privkey.pem ]; then
echo "TLS certificate present, leaving it alone."
exit 0
fi
apk add --no-cache openssl >/dev/null
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /ssl/privkey.pem -out /ssl/fullchain.pem \
-subj "/CN=${FORAIL_TLS_CN:-localhost}" >/dev/null 2>&1
echo "WARNING: generated a self-signed TLS certificate for ${FORAIL_TLS_CN:-localhost}."
echo "WARNING: browsers will not trust it. Replace nginx/ssl/ before production."
volumes:
- ./nginx/ssl:/ssl
restart: "no"

nginx:
image: nginx:1.27-alpine
ports:
Expand All @@ -207,6 +234,8 @@ services:
networks:
- forail
depends_on:
nginx-cert-init:
condition: service_completed_successfully
forail-web:
condition: service_healthy
forail-frontend:
Expand Down
Loading