> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting with Docker

> Run the daily, push and weekly routines on a server of your own, Railway or Fly.io, without GitHub Actions.

Everything the [GitHub workflows](https://docs.appeeky.com/docs/spec-ci.md) do lives in the repo, so any machine that runs Docker can do it. Each routine clones the repo, does one thing, commits what it wrote and exits. The container keeps nothing between runs, and nothing listens on a port: it only calls out to GitHub, Appeeky, the stores and, when you set their keys, Claude and fal.

## Images

| Image | Use it for |
| - | - |
| `ghcr.io/appeeky/appeeky-spec:latest` | `daily` and `push` |
| `ghcr.io/appeeky/appeeky-spec:latest-agent` | The same, plus Claude Code and `gh` for `weekly` |

Each release also publishes `:<version>` and `:<version>-agent`. To build it yourself from the [repo](https://github.com/appeeky/appeeky-spec): `docker build -f docker/Dockerfile -t appeeky-spec .` (`--build-arg AGENT=1` for the agent).

## Routines

| Mode | Run it | Does |
| - | - | - |
| `daily` | Once a day | `category pull`, `visibility pull`, `verify --playbook`, `reviews pull`, `competitors pull`, `social pull`, `ugc pull`, and the pulls on your own keys that are set; one commit |
| `push` | Every 10–15 minutes | Pushes listing changes merged to `main`, sends approved posts and clips, commits the release |
| `weekly` | Once a week | The [co-founder review](https://docs.appeeky.com/docs/spec-growth-team.md): opens a pull request with a brief and one listing change. Needs the `-agent` image |
| `schedule` | Always on | Stays up and runs the three above on a schedule |

Pick one as the container command or with `MODE=<routine>`.

## On your own server

Put [`docker/docker-compose.yml`](https://github.com/appeeky/appeeky-spec/blob/main/docker/docker-compose.yml) and a `.env` filled from [`docker/.env.example`](https://github.com/appeeky/appeeky-spec/blob/main/docker/.env.example) in one folder:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
services:
  appeeky-spec:
    image: ghcr.io/appeeky/appeeky-spec:latest   # or :latest-agent
    command: schedule
    env_file: .env
    restart: unless-stopped
```

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
docker compose up -d       # runs the routines on a schedule, and again after a reboot
docker compose logs -f
docker compose run --rm appeeky-spec daily   # one routine by hand
```

The first log lines show the schedule and where `push` starts following `main`. Any Linux VM works; the routines run for a minute and exit, so the smallest plan is enough. Give the weekly review a little more memory. For more than one app, copy the service and give each its own env file.

## .env

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
REPO_URL=https://github.com/you/your-app.git
GITHUB_TOKEN=…                     # contents write on that repo (pull requests write for weekly)
APPEEKY_API_KEY=…                  # store access, analytics, AI Visibility (optional with your own keys)
```

| Variable | Meaning |
| - | - |
| `REPO_URL` | The app's repo; any git URL |
| `GITHUB_TOKEN` | A fine-grained token for that one repo: Contents read and write, plus Pull requests read and write for `weekly`. Passed to git through a credential helper, never in a URL or a log |
| `BRANCH` | Default `main` |
| `SPEC_DIR` | Folder with `appeeky.spec.yaml` inside the repo; default `.` |
| `DAILY_AT` | Cron, UTC; default `0 6 * * *` |
| `PUSH_EVERY` | Cron, UTC; default `*/15 * * * *`; `off` to push by hand |
| `WEEKLY_AT` | Cron, UTC, for example `0 7 * * 1`; off by default |
| `CLAUDE_CODE_OAUTH_TOKEN` | `weekly`: run `claude setup-token` on your computer to use your Claude Pro or Max plan |
| `ANTHROPIC_API_KEY` | `weekly`: or pay per use. Also adds Claude to `visibility scan --local` |
| `HEALTHCHECK_URL` | Pinged after every daily run, `<url>/fail` when it fails (healthchecks.io style) |
| `PUSH_DRY_RUN` | `1`: show what `push` would change, change nothing |
| `ATTRIBUTION_TOKEN` | A self-hosted attribution server |
| `FAL_KEY` | Make UGC clips here on your own fal account |
| `GSC_CREDENTIALS` | `seo pull` |
| `ASA_CLIENT_ID`, `ASA_TEAM_ID`, `ASA_KEY_ID`, `ASA_PRIVATE_KEY` | `search-ads pull` |
| `DATAFORSEO_LOGIN`, `DATAFORSEO_PASSWORD`, `SEO_RANKS_EVERY` | `seo ranks` every N days (default 7) |
| `VISIBILITY_EVERY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `PERPLEXITY_API_KEY` | `visibility scan --local` every N days |

What each own key does and costs: [On your own keys](https://docs.appeeky.com/docs/spec-own-keys.md).

## How push works

`push` remembers the last commit it handled as the `appeeky-spec/pushed` tag in the repo. Replace the machine and it carries on from there.

* **The first run only sets the tag.** Whatever is on `main` today is never pushed; only merges after that are.
* When a merge adds social drafts or UGC briefs, it sends the posts and makes the clips (`--local` when `FAL_KEY` is set), then commits.
* When a merge changes `appeeky/store/` or `appeeky.spec.yaml`, it runs `push --yes` with the commit subject as the note, and commits the release.
* A change the store cannot take yet (a new name or subtitle without a new App Store version) keeps the tag where it was, so the next run tries again once the version exists. A failed run also leaves the tag.

## Railway and Fly.io

Deploy the same image as a worker with no public port, set the variables from the `.env` file as service variables, and use `schedule` as the start command. It behaves exactly like the VM. Render and other container hosts work the same way.

## With a scheduler you already have

Cron, Cloud Run Jobs, a Kubernetes CronJob, Railway cron: run one routine per job.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
docker run --rm --env-file app.env ghcr.io/appeeky/appeeky-spec daily
docker run --rm --env-file app.env ghcr.io/appeeky/appeeky-spec push
docker run --rm --env-file app.env ghcr.io/appeeky/appeeky-spec:latest-agent weekly
```

## A safe first week

| | |
| - | - |
| **Nothing old goes out** | The first `push` run starts from what is on `main` today and never pushes it. |
| **Dry run first** | With `PUSH_DRY_RUN=1`, `push` prints what it would change and the posts and clips it would send, and changes nothing. Remove it when the output looks right. |
| **Know when it fails** | Set `HEALTHCHECK_URL` so a missed or failed daily run sends you an email. |
| **Logs stay small** | `docker-compose.yml` rotates them at 10 MB. |
| **Pull request comments** | They still need GitHub: keep `listing-check.yml` from `init --ci` for those. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.