Regalamiunsorriso/faceai/docs/forgejo-autodeploy-options.md

9.9 KiB

Forgejo FaceAI Autodeploy Options

Goal

Make the existing Forgejo image publish workflow complete the last step automatically by redeploying the FaceAI runtime after new images are pushed.

Verified Current State

Repository

  • .forgejo/workflows/publish-faceai-container.yml already builds and pushes two images on master pushes:
    • faceai-client
    • faceai-processor
  • The workflow tags both images as:
    • sha-<short-sha>
    • latest
  • The production-style compose definitions in faceai/docker-compose.yml and stacks/faceai.yml currently consume the :latest tags.

Target Host

Read-only inspection on root@pve02docker.maddo.science confirmed:

  • the host is running Docker and Portainer
  • the FaceAI services are already live:
    • regalami-faceai
    • regalami-faceai-processor
    • regalami-faceai-redis
  • the running FaceAI containers are managed by Docker Compose from /data/compose/4
  • the compose file in use is /data/compose/4/docker-compose.yml
  • Portainer is present on the host, but the FaceAI containers did not expose an io.portainer.stack.id label during inspection

This matters because the current deployment looks Compose-first. Portainer is available, but the cleanest automation path does not need to depend on Portainer unless we explicitly want it to.

What "automatic deploy" means here

Because the server is already running the correct images and compose stack, the deployment action is simply:

cd /data/compose/4
docker compose pull
docker compose up -d

In practice, the workflow should usually limit this to the services that actually change:

cd /data/compose/4
docker compose pull faceai processor
docker compose up -d faceai processor

That leaves Redis untouched unless the compose definition itself changes.

Options

Option 1: Deploy over SSH directly from the Forgejo workflow

Add a second job after image publish that opens an SSH session from the Forgejo runner to the target server at 192.168.10.69 and runs the compose pull and redeploy commands.

Typical flow:

  1. build and push images
  2. SSH to the deployment host
  3. run docker compose pull faceai processor
  4. run docker compose up -d faceai processor
  5. verify with docker compose ps and a health check or recent logs

Why this fits the current setup:

  • it matches the host's current Compose-managed reality
  • it does not require Portainer-specific features
  • it is the smallest change from today's manual rollout
  • it is deterministic: deploy happens only when the publish job succeeds

Prerequisites:

  • the Forgejo runner must be able to reach 192.168.10.69
  • the workflow needs an SSH private key stored as a secret
  • the target host needs a user that can run the required Docker Compose commands
  • the target host must already have registry credentials that allow docker compose pull, or the workflow must also perform a remote docker login

Advantages:

  • simplest and most explicit option
  • easy to audit in workflow logs
  • easy to gate with branch rules or manual approvals later if needed
  • easiest option to verify with post-deploy health checks

Disadvantages:

  • the CI system receives deployment credentials
  • network reachability from runner to host becomes part of deployment reliability
  • if latest is reused, the workflow is still deploying a mutable tag rather than an immutable release reference

Implementation notes:

  • use a separate deploy job with needs: publish
  • add a Forgejo concurrency group for production deploys so two pushes do not race each other
  • prefer a dedicated deploy user over root
  • if possible, restrict the SSH key with a forced command or a narrowly scoped sudo rule

Option 2: Trigger Portainer API redeploy from the workflow

Instead of SSH, let the workflow call the Portainer HTTP API after the images are pushed.

Typical flow:

  1. build and push images
  2. authenticate to Portainer with an API token
  3. call the relevant stack redeploy endpoint
  4. let Portainer pull the latest tagged images and recreate the stack

Why this can work:

  • Portainer is already running on the host
  • Portainer's API is intended for automation
  • this keeps the workflow talking to one control-plane API instead of opening SSH

Advantages:

  • clean control-plane integration if Portainer is the intended long-term operations surface
  • centralizes stack redeploy operations in Portainer
  • avoids distributing shell-level access to the host into CI

Disadvantages:

  • requires Portainer API token management
  • requires knowing the correct environment and stack identifiers
  • if the current FaceAI deployment is not fully Portainer-native, this adds a second management path on top of the existing Compose-first one
  • troubleshooting usually becomes more Portainer-specific

Practical caveat for this host:

The current FaceAI containers clearly show Compose management from /data/compose/4, while the inspection did not confirm an active Portainer stack label on them. That does not make Portainer API automation impossible, but it does mean this path should be treated as an intentional operational redesign rather than the smallest incremental step.

Option 3: Trigger a Portainer stack webhook from the workflow

Portainer supports stack webhooks that can redeploy a stack when an HTTP POST is sent to the webhook URL.

Typical flow:

  1. enable a stack webhook in Portainer
  2. store the webhook URL as a Forgejo secret
  3. curl -X POST the webhook at the end of the publish workflow

Advantages:

  • very simple workflow-side integration
  • no SSH session needed
  • easier to wire than the full Portainer API

Disadvantages:

  • Portainer documents stack webhooks as a Business Edition feature
  • webhook security is mostly the secrecy of the URL
  • still depends on the stack being managed correctly in Portainer

Assessment:

This is attractive only if the host is using Portainer Business Edition and you want the shortest possible HTTP-based trigger. Otherwise it is not the best fit.

Option 4: Host-side auto-updater such as Watchtower

Run a container on the target host that watches for updated image tags and automatically recreates containers when a newer image appears in the registry.

Typical flow:

  1. Forgejo builds and pushes new latest
  2. Watchtower notices the new image
  3. Watchtower pulls and restarts the affected containers automatically

Advantages:

  • decouples deployment from Forgejo runner network reachability
  • zero extra deploy step in the workflow
  • simple mental model if the policy is always "follow latest"

Disadvantages:

  • less explicit and less deterministic than a pipeline-controlled deploy
  • easier to deploy at unexpected times if tags move
  • broader blast radius unless carefully scoped to only the FaceAI containers
  • can drift away from the compose-driven operational model

Assessment:

This works, but it is better for "always keep these containers on the newest tag" than for controlled CI/CD. It is not the best match if you want the workflow run itself to be the audited deployment event.

Option 5: Host-side scheduled pull and redeploy

Use a cron job or systemd timer on the host to periodically run:

cd /data/compose/4
docker compose pull faceai processor
docker compose up -d faceai processor

Advantages:

  • very small operational footprint
  • no Portainer API and no CI-to-host SSH required
  • resilient if the Forgejo runner cannot reach the host directly

Disadvantages:

  • deploy timing is delayed and approximate
  • weak audit trail compared with a pipeline-driven deploy
  • no natural coupling between a successful image publish and the rollout itself

Assessment:

This is acceptable as a fallback, but it is not true autonomous CD tied to a specific successful workflow run.

Recommendation

The best fit for the current system is Option 1: direct SSH deploy from the Forgejo workflow.

Reasoning:

  • the host is already running the stack through plain Docker Compose
  • the required deploy action is just docker compose pull followed by docker compose up -d
  • this keeps the delivery path explicit and easy to reason about
  • it avoids introducing Portainer-specific coupling when Portainer is not currently the proven source of truth for the FaceAI runtime

If you want the smallest reliable implementation, keep the current :latest tag contract and add a deploy job that runs only after both image pushes succeed.

If you want the more rigorous version later, evolve the stack from :latest to immutable sha-... tags and have the workflow update the deployed tag intentionally before redeploying. That is a second hardening step, not a requirement for getting autonomous CD working now.

Suggested Minimal Rollout Design

  1. Keep the current publish job.
  2. Add a deploy job with needs: publish.
  3. Store deployment SSH credentials in Forgejo secrets.
  4. SSH to 192.168.10.69 and run:
cd /data/compose/4
docker compose pull faceai processor
docker compose up -d faceai processor
docker compose ps
docker logs --tail 100 regalami-faceai
docker logs --tail 100 regalami-faceai-processor
  1. Fail the job if the compose update or health verification fails.
  2. Add workflow concurrency so only one production deploy runs at a time.

Open Questions Before Implementation

  • Which host user should the workflow use for deployment?
  • Does the target host already have persistent registry credentials for forgejo.maddoscientisto.net?
  • Is the Forgejo runner allowed to reach 192.168.10.69 directly over SSH, or does it need a different path?
  • Do you want the initial autonomous version to keep using latest, or do you want to switch immediately to immutable image tags?

Bottom Line

Yes, full autonomous CD is feasible with the current setup.

The shortest safe path is not "make Portainer do it" but "let the workflow SSH to the Compose host and run the two compose commands after a successful image push". Portainer-based automation is possible, but it is a weaker fit for the current deployment shape unless you specifically want Portainer to become the authoritative deployment control plane.