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

# Your first site

> Create your repo, build your image, deploy your site

The [Quickstart](/quickstart) deploys the upstream demo image. This guide
walks the **builder** path: create your own copy of
[`site-template`](https://github.com/frankenpress/site-template),
let it self-customise, push your own image, and deploy that.

We'll call your new repo **`mysite`** throughout — substitute whatever
name you actually pick (`my-blog`, `acme-marketing`, etc.). Same for
**`<owner>`** — your GitHub account or organisation.

## Prerequisites

<AccordionGroup>
  <Accordion title="Required tools" icon="screwdriver-wrench">
    * **GitHub** account with permission to create public repos and packages
    * **Docker Desktop** or [OrbStack](https://orbstack.dev/) (macOS), or Docker Engine on Linux
    * **`git`**, **`make`**, **`composer`**
    * **`gh`** ([GitHub CLI](https://cli.github.com/)) — optional, but the docs use it for terseness
    * **`kind`** + **`kubectl`** + **`helm`** ≥ v3.8 — only needed for the cluster step
  </Accordion>

  <Accordion title="What gets created on your account" icon="github">
    * A new repo at `github.com/<your-account>/<your-site>` (public by default — flip to private if you prefer; cluster-side pulls of a private GHCR package need credentials, e.g. node-level containerd auth on Talos or a per-namespace `imagePullSecret`)
    * A GHCR package at `ghcr.io/<your-account>/<your-site>` — built and tagged automatically by CI
  </Accordion>
</AccordionGroup>

<Steps>
  <Step title="Create your site repo">
    GitHub gives you two ways to derive a new repo from
    `site-template`. Pick whichever matches how closely you want to
    track upstream.

    <Tabs>
      <Tab title="Use this template (recommended)">
        Click **[Use this template](https://github.com/frankenpress/site-template/generate)**
        on `site-template`. Pick an owner and the name `mysite`
        (or whatever you prefer).

        * **Pros.** Clean new repo without the template's full git history.
          No "forked from" badge in the GitHub UI. You explicitly choose
          which upstream changes to pull in by cherry-picking — easier
          to keep your repo's history representative of *your* site
          rather than the template's churn.
        * **Cons.** No "Sync fork" button. To pull upstream improvements
          (CI tweaks, lockdown changes, etc.) you add the template as
          a remote and cherry-pick:

          ```bash theme={null}
          git remote add upstream https://github.com/frankenpress/site-template
          git fetch upstream
          git log --oneline upstream/main ^main | head      # what's new
          git cherry-pick <sha>...<sha>                     # adopt selectively
          ```

        Best for production-track sites that will diverge from the
        template over time.
      </Tab>

      <Tab title="Fork">
        Click **[Fork](https://github.com/frankenpress/site-template/fork)**
        on `site-template`. Pick an owner and the name `mysite`.

        * **Pros.** Shared git history with the template. The "Sync
          fork" button in the GitHub UI does the right thing for trivial
          upstream tracking. Easier when your site stays close to the
          template shape.
        * **Cons.** GitHub shows a "forked from" badge. Can't be made
          private if `site-template` stays public, without using
          GitHub's private-fork dance. "Sync fork" auto-merges *every*
          upstream commit, which isn't always what you want for a
          production site — once you have meaningful divergence,
          you'll be cherry-picking anyway.

        Best for read-along-with-upstream demos, training environments,
        and short-lived experiments.
      </Tab>
    </Tabs>

    Whichever you pick, you'll end up with a new repo at
    `github.com/<owner>/mysite`.

    Within \~30 seconds GitHub Actions runs the **template-cleanup**
    workflow on your new repo. It does three things:

    1. Substitutes the upstream-hardcoded fields:
       * `composer.json`: `name`, `description`, `homepage`, `support.issues`, `support.source`
       * `Dockerfile`: `org.opencontainers.image.source` label
    2. Removes the cleanup workflow itself (one-shot).
    3. Triggers `build.yml` to publish the first image.

    You'll see two commits on `main`: the original template snapshot
    and `chore: initialize from site-template`. Watch progress with:

    ```bash theme={null}
    gh run watch --repo <owner>/mysite
    ```

    <Check>
      After \~2 minutes, `ghcr.io/<owner>/mysite:latest` is pullable
      and the workflows tab shows green `template-cleanup` + `build` runs.
    </Check>
  </Step>

  <Step title="Clone and run locally">
    ```bash theme={null}
    git clone git@github.com:<owner>/mysite.git
    cd mysite
    fp init
    ```

    `fp init` is a one-command onboarding helper — it scaffolds `.env`
    from `.env.example`, runs `composer install` via docker, brings
    the stack up (`docker compose up -d --wait`), installs WordPress
    with sensible local defaults, and applies the latest committed
    snapshot (if any). Re-run it after `docker compose down -v` to
    recover from a wiped local stack.

    Open `http://localhost:8080/wp/wp-admin/` and log in
    (`admin / admin` by default; override via `[init]` in
    `frankenpress.toml`).

    <Note>
      Lower-level alternative if you'd rather drive it manually:

      ```bash theme={null}
      make setup
      make up
      make wp ARGS="core install \
        --url=http://localhost:8080 \
        --title='My Site' \
        --admin_user=admin \
        --admin_email=admin@example.com \
        --admin_password=admin \
        --skip-email"
      ```

      `make wp` runs wp-cli in the site container with
      `--allow-root --path=/app/web/wp` (WordPress core lives under
      `web/wp/` per the Bedrock layout — wp-cli won't find it
      otherwise). Pass your sub-command + flags through `ARGS="…"`.
    </Note>

    <Note>
      Local Docker Compose is the only place you need to install
      WordPress manually (or via `fp init`). When you deploy via the
      Helm chart (the next steps), the chart runs `wp core install`
      automatically as a post-install hook Job — see [charts →
      First install](/components/charts#first-install).
    </Note>

    <Check>
      The "Update WordPress" / "Update Plugins" / "Update Themes"
      buttons should be **absent**. The lockdown is hard-coded in
      `config/application.php` — see [site-template → Lockdown](/components/site-template#lockdown).
    </Check>

    Upload a media item under **Media → Add New**. It lands in MinIO
    at `http://localhost:9001` (credentials `minioadmin` / `minioadmin`).

    <Tip>
      `make logs` tails the site container. `make shell` drops you into
      it. `make down` tears the stack down (volumes preserved); `make
                  clean` wipes volumes too.
    </Tip>
  </Step>

  <Step title="Cut a release">
    The template's `build.yml` publishes images on every push to `main`
    (`:latest` + `:<short-sha>`) and on every `v*.*.*` tag (`:vX.Y.Z`
    additionally). Tag your first release:

    ```bash theme={null}
    git tag v0.1.0
    git push origin v0.1.0
    ```

    Watch the build:

    ```bash theme={null}
    gh run watch
    ```

    <Check>
      Three image tags now exist on `ghcr.io/<owner>/mysite`:

      * `:latest` (moves on every main push)
      * `:<short-sha>` (immutable per commit)
      * `:v0.1.0` (immutable per tag — use this in production)
    </Check>

    <Tip>
      Your site builds against **PHP 8.3** by default. To pin to a
      different supported PHP, see [Runtime matrix](/operations/runtime-matrix#picking-a-php).
    </Tip>
  </Step>

  <Step title="Deploy to a cluster">
    Same `site` chart as the [Quickstart](/quickstart), pointed at
    your image:

    ```bash theme={null}
    kind create cluster --name myfp
    kubectl config use-context kind-myfp

    helm install mysite oci://ghcr.io/frankenpress/charts/site \
      --version 0.13.2 \
      --namespace mysite --create-namespace \
      --set image.repository=ghcr.io/<owner>/mysite \
      --set image.tag=v0.1.0 \
      --set site.url=http://localhost:8080 \
      --set site.env=development \
      --wait --timeout=10m
    ```

    <Note>
      `--set site.url=http://localhost:8080` matches the port-forward
      URL below; `--set site.env=development` keeps `FORCE_SSL_ADMIN=false`
      so `/wp/wp-admin/` doesn't 302 to a `https://` URL the port-forward
      isn't serving. See [Quickstart](/quickstart) for the full
      explanation. For a real deploy, drop both overrides and set
      `site.url` to your public HTTPS URL.
    </Note>

    The chart runs `wp core install` automatically as a post-install
    hook Job — see [`charts` → First install](/components/charts#first-install)
    for the admin credentials, bring-your-own-Secret, and rotation
    options.

    Port-forward and log in:

    ```bash theme={null}
    # Retrieve the auto-generated admin password
    kubectl --namespace mysite get secret mysite-install \
      -o jsonpath='{.data.admin_password}' | base64 -d

    kubectl --namespace mysite port-forward svc/mysite 8080:80 &
    ```

    Open `http://localhost:8080/wp/wp-admin/` and log in as `admin`.

    <Tip>
      The default install brings up bundled MariaDB + Redis + MinIO
      subcharts — fine for kind, **not for production**. Swap them out
      per [Production topology](/operations/production).
    </Tip>
  </Step>
</Steps>

## What's next

You've got `mysite` building, deployed, and serving. The natural next
step is making it yours — adding plugins, picking a theme, swapping
defaults.

<CardGroup cols={2}>
  <Card title="Customizing your site" icon="puzzle-piece" href="/customizing">
    Add and remove plugins and themes the FrankenPress way — composer at build time, immutable image tag, GitOps deploy.
  </Card>

  <Card title="Production topology" icon="cloud" href="/operations/production">
    Replace bundled subcharts with operator-managed databases + AWS S3.
  </Card>

  <Card title="All env vars" icon="list" href="/operations/configuration">
    Every `FP_*` and `WP_*` variable the platform reads, with defaults.
  </Card>

  <Card title="Upgrade flow" icon="arrows-rotate" href="/operations/upgrade">
    `git tag` → CI → image build → `helm upgrade --set image.tag=...` per environment.
  </Card>
</CardGroup>
