Skip to content

Quickstart

This guide walks you through wiring reoclo/deploy-sync@v2 into a workflow that already runs docker compose up or docker run on a Reoclo-managed server. The action is published on the GitHub Marketplace; pin to @v2 for the latest v2.x release or to an exact tag for reproducible builds.

  1. Generate an automation API key with the external_deploy scope

    In the Reoclo dashboard:

    1. Navigate to API Keys, then switch to the Automation Keys tab.
    2. Click Create Automation Key.
    3. Give it a name like github-deploy-sync-prod.
    4. Under Allowed Operations, enable external_deploy.
    5. Optionally restrict the key to a specific server and set an IP allowlist.
    6. Click Create and copy the key immediately — it is shown only once.

    The key will begin with rca_.

  2. Add the key to your GitHub repository secrets

    In your GitHub repository, go to Settings > Secrets and variables > Actions and add:

    • REOCLO_API_KEY — the rca_ key you just created.
  3. Add the sync step to your workflow

    Insert the reoclo/deploy-sync@v2 step immediately after your docker compose up or docker run step:

    name: Deploy
    on:
    push:
    branches: [main]
    jobs:
    deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Deploy containers
    uses: reoclo/run@v2
    with:
    api_key: ${{ secrets.REOCLO_API_KEY }}
    server_id: ${{ secrets.REOCLO_SERVER_ID }}
    command: |
    cd /srv/reoclo/myapp
    docker compose -f docker-compose.prod.yml pull
    docker compose -f docker-compose.prod.yml up -d
    timeout: 300
    - name: Sync routes with Reoclo
    uses: reoclo/deploy-sync@v2
    with:
    api_key: ${{ secrets.REOCLO_API_KEY }}
    compose_file: ./docker-compose.prod.yml

    /srv/reoclo/myapp/ is the recommended on-server location — the runner installer pre-creates /srv/reoclo/ with a shared reoclo-deploy group so both the runner and your human admins can write to it without chown. See Deployment Folder & Permissions for the full layout.

    The action reads docker-compose.prod.yml from the runner workspace to discover which services to sync. Only services on the reoclo-proxy network or labeled reoclo.managed=true are considered; others are reported as unmatched and skipped.

    If you are not using Docker Compose, pass an explicit service list instead:

    - uses: reoclo/deploy-sync@v2
    with:
    api_key: ${{ secrets.REOCLO_API_KEY }}
    services: "myapp-web:3000,myapp-api:8080"
  4. Bind each routed service to its Application

    Reoclo needs to know which Application a discovered service maps to. The container name it receives is the service’s container_name: or, if unset, the Compose service key (e.g. app) — which is almost never the running container name (<project>-<service>-<index>, e.g. myapp-app-1). So binding by name alone is fragile and is the usual cause of a 400: None of the supplied identifiers matched an application.

    Recommended: label the service with your Application’s slug so identity no longer depends on the container name:

    services:
    app:
    image: ghcr.io/myorg/app:${{ github.sha }}
    networks: [reoclo-proxy]
    expose: ["3000"]
    labels:
    reoclo.app: my-application-slug # bind to this Application by slug

    Alternatives: set an explicit container_name: and link the Application to that exact name, or add a linked_container_names alias / linked_container_name_pattern glob to the Application. See Matching a service to its Application.

  5. Run the workflow and confirm in the dashboard

    Push a commit to trigger the workflow. After the sync step succeeds you should see:

    • The step summary shows status: synced for each matched Application and lists the FQDNs that were updated.
    • In the Reoclo dashboard, the Application’s Proxy tab shows a refreshed upstream pointing at the current container name and port.
    • Visiting the Application’s FQDN returns a live response from the freshly deployed container.

    If a container name is not matched to any Application, the step logs unmatched: [container_name] and succeeds without error — unrecognized containers are silently skipped.

ScenarioStatusEffect
Same container name, same port, same image tagnoopNo proxy changes; Caddy not triggered
Same container name, different port or image tagsyncedProxy rewritten, Caddy reconciler triggered
Different configuration within 30 seconds of a previous syncconflictNo changes; pass force: true to override