# Installing Skyline

> Authenticate Composer with your license key, add the registry, and install.

Source: https://boring-observability.dev/skyline/docs/installation
Section: Getting started — Skyline for Laravel documentation
Updated: 2026-07-13

---

Skyline is distributed through a private Composer registry rather than Packagist. Installation is three steps: authenticate Composer with your license key, point `composer.json` at the registry, then require the package and run Horizon's installer as usual.

## 1. Authenticate Composer with your license key

[Buying a license](https://checkout.anystack.sh/laravel-skyline) through [Anystack](https://anystack.sh/) gives you a key that grants access to the private registry at `laravel-skyline.composer.sh`. Run this once on every machine that pulls the package — your development machine and any CI or build environment:

```bash
composer config --global --auth http-basic.laravel-skyline.composer.sh \
  you@example.com \
  YOUR-LICENSE-KEY
```

The username is the licensee email tied to the purchase, and the license key acts as the password. This writes the credentials to `~/.composer/auth.json` (or `$COMPOSER_HOME/auth.json`).

### CI and production servers

For servers and CI you can either commit an `auth.json` alongside `composer.json`, or — better, since it keeps the key out of the repository — supply the credentials through the `COMPOSER_AUTH` environment variable:

```bash
export COMPOSER_AUTH='{"http-basic":{"laravel-skyline.composer.sh":{"username":"you@example.com","password":"YOUR-LICENSE-KEY"}}}'
```

> **Treat the key as a secret**
>
> The license key is a credential. Store it in your secret manager or CI secrets, and never commit `auth.json` to a public repository.

## 2. Add the registry and require the package

Add the repository to your application's `composer.json`:

```json
{
    "repositories": [
        { "type": "composer", "url": "https://laravel-skyline.composer.sh" }
    ]
}
```

Then require it:

```bash
composer require boring-o11y/laravel-skyline
```

Skyline declares `replace: laravel/horizon`, so any package or application code that depends on `laravel/horizon` resolves to Skyline without further changes. Composer will refuse to install both at once — that is the intended behaviour.

## 3. Publish assets and run

```bash
php artisan horizon:install
php artisan horizon
```

The dashboard mounts at `/horizon`, and the published `config/horizon.php` is the stock Horizon file plus a handful of new keys. Nothing else is required to get the Skyline features — see [Configuration](https://boring-observability.dev/skyline/docs/configuration) for what you can tune.

If this is a new Horizon installation, remember to schedule the metrics snapshot command, exactly as upstream Horizon requires. In `routes/console.php`:

```php
use Illuminate\Support\Facades\Schedule;

Schedule::command('horizon:snapshot')->everyFiveMinutes();
```

## Upgrading from Laravel Horizon

Because Skyline keeps the `Laravel\Horizon\` namespace and replaces `laravel/horizon`, the upgrade needs no code changes at all:

1. Remove `laravel/horizon` from the `require` block of `composer.json`.
2. Add the Anystack registry and `boring-o11y/laravel-skyline`, as shown above.
3. Run `composer update`.
4. Redeploy and restart your workers (`php artisan horizon:terminate`).

Your existing `config/horizon.php`, service-provider customisations, gate definitions and references to `Laravel\Horizon\Horizon` all continue to work. Skyline ships its own compiled dashboard assets, so re-run `php artisan horizon:publish` as part of your deploy if you publish Horizon's assets to `public/`.

> **Rolling back**
>
> Rolling back is the same operation in reverse: require `laravel/horizon` again and drop the Skyline package. Skyline stores its extra tracking data under the same Redis prefix and adds no schema, so a rollback loses the Skyline-only views but leaves your queues and jobs untouched.

## Automated updates with Dependabot

Dependabot can raise pull requests for new Skyline releases, but only if it can authenticate against `laravel-skyline.composer.sh` — it does not read your local `auth.json`. Declare the registry in `.github/dependabot.yml` and reference it from the Composer update entry:

```yaml
version: 2

registries:
  skyline:
    type: composer-repository
    url: https://laravel-skyline.composer.sh
    username: ${{secrets.SKYLINE_LICENSE_EMAIL}}
    password: ${{secrets.SKYLINE_LICENSE_KEY}}

updates:
  - package-ecosystem: composer
    directory: "/"
    registries:
      - skyline
    schedule:
      interval: weekly
```

The `username` is the licensee email tied to the purchase and the `password` is the license key — the same pair you passed to `composer config --auth` above. An update entry only sees the registries it names, so the `registries: [skyline]` list is required; without it Dependabot resolves `boring-o11y/laravel-skyline` against Packagist, fails to find it, and skips the whole Composer manifest rather than just that one package.

> **Dependabot secrets are not Actions secrets**
>
> `${{secrets.*}}` in `dependabot.yml` resolves against **Dependabot** secrets, stored under Settings → Secrets and variables → **Dependabot**. A secret of the same name under *Actions* is invisible to Dependabot, which is the usual reason an otherwise-correct config still 401s.

The same split affects CI on the pull requests Dependabot opens: workflow runs it triggers are given the Dependabot secrets, not the Actions ones. If your build runs `composer install`, the credentials it reads — typically `COMPOSER_AUTH`, as under [CI and production](#ci-and-production) above — must also exist as Dependabot secrets, or Skyline's own update PRs will be the only ones that fail to install.

## Upgrades and renewal

Your purchase includes twelve months of upgrades, and during them `composer update` pulls every new release. After that the versions published in your window remain available to install — already-installed copies keep running normally, because there is no runtime license check that can interrupt job processing. Only releases published after your upgrade year fail to resolve, until you renew. License management, billing and renewal are handled through your Anystack account.


## Common questions

### How do I install Skyline for Laravel?

Authenticate Composer with your license key, add the https://laravel-skyline.composer.sh repository to composer.json, then run composer require boring-o11y/laravel-skyline. Finish with php artisan horizon:install and php artisan horizon, exactly as you would with Horizon.

### Why does Composer return a 401 for laravel-skyline.composer.sh?

The machine running Composer has no valid credentials. The username is the licensee email tied to the purchase and the license key acts as the password. On CI and build servers, supply them through the COMPOSER_AUTH environment variable rather than committing auth.json to the repository.

### Can Dependabot raise pull requests for Skyline releases?

Yes, but it needs its own credentials: declare the registry in .github/dependabot.yml and reference it from the Composer update entry. The secrets it interpolates are Dependabot secrets, not Actions secrets — a secret of the same name under Actions is invisible to Dependabot, which is the usual reason an otherwise-correct config still returns 401.
