# Fornace hosting

Shared hosting operated by Pi through Matchbox. The platform accepts static sites, Next.js/Node applications, Rust services and maintained PHP applications. Each site has its own container processes, private network, data directory and CPU, memory and disk ceilings. Databases are created only when the application needs them.

## Connect

Ask your Pi to load the current shared `matchbox` skill and connect to `fornace-hosting` using your own enrolled identity:

```sh
fornace-matchbox assert
fornace-ssh fornace-hosting -- 'hostname; hostingctl --help'
```

Root access is assigned to Francesco, Luca, Simone, Riccardo, Maria Luisa, Fabrizio, Martin and Piermaria. Francesco and Luca retain their existing owner permissions. Root operators can change every site; application containers do not receive host access or the Docker socket.

The source is the private repository `Fornace/fornace-hosting`. Changes merged into `main` deploy through GitHub Actions. Keep host manifests and secrets outside the source checkout so releases do not overwrite them.

## Create a site

Build applications in their own repository's CI, not on the hosting box. Supply the resulting Linux amd64 OCI image. Static sites use the files exported by their build. Runtime is a per-site choice; changing the host is not necessary to add Rust or Next.js.

```sh
# Static: use the actual exported directory, not a demonstration page.
hostingctl create static example.com --source /root/incoming/example.com --disk 2G --cpu 0.25 --mem 64M

# Next.js: build with output=standalone, listen on 0.0.0.0:3000.
hostingctl create node app.example.com --image REGISTRY/APP@sha256:DIGEST --disk 5G --cpu 1 --mem 1G --app-port 3000

# Rust: use a release binary in a non-root runtime image.
hostingctl create rust api.example.com --image REGISTRY/API@sha256:DIGEST --disk 2G --cpu 1 --mem 256M --app-port 8080

# WordPress/PHP: request a database only when needed.
hostingctl create php example.com --php-version 8.5 --with-db --source /root/incoming/example.com --baseline-bytes MEASURED_LIVE_BYTES
```

Replace all illustrative domains, image names and sizes with the real site's inputs. OCI references and runtime versions are locked when applied. PHP extensions required by the application belong in its maintained image. No ionCube or obsolete PHP runtime is provisioned.

Configure the site's A record to `195.201.175.87` through the domain's existing DNS provider and granted operation. `hostingctl` prints the required records; it does not assume permission over customer zones. Caddy issues and renews HTTPS for explicitly registered domains. Do not change customer MX records or nameservers merely to move web hosting.

## Limits and storage

For existing sites, measure **live files plus database storage**, excluding exact old backup paths. Pass the measured bytes once: the disk ceiling is twice that baseline, rounded to filesystem units. This is a limit, not reserved space. New sites receive an explicit disk ceiling. Monitor actual shared free space separately from the sum of quotas.

CPU and RAM ceilings cover the site's application, database and scheduled jobs together. Static sites also run in their own limited container. A container that exhausts its own quota cannot write past it. The host's kernel, disks and network remain shared.

Persistent files live under `/srv/sites/<site-id>/`. Configuration and private credentials live under `/etc/fornace-hosting/`. Images and container engine state have a separate filesystem. Do not add unmanaged named volumes or put client files in container writable layers.

## Operate through Pi

```sh
hostingctl status
hostingctl configure SITE_ID --cpu 1 --mem 1G --disk 10G --apply
hostingctl update SITE_ID --image REGISTRY/APP@sha256:DIGEST
hostingctl exec SITE_ID -- COMMAND
hostingctl quota table
hostingctl logs SITE_ID
```

Use `hostingctl --help` for the installed command contract. Keep scheduled commands inside the site's resource slice. Application secrets are private host files, not Git commits, command output or public guide examples.

Deletion stops the site but preserves its data unless the operator explicitly requests data removal. Never delete a source server merely because files have been copied.

## Migrate from Plesk or AWS

1. Coordinate ownership with Luca before touching AWS sites he is moving. Record the domain, source, destination and responsible operator in Simone's existing spreadsheet.
2. Read the site's current runtime from its live server. The old version columns are not a support matrix.
3. Copy live document roots and application state directly to the final quota-bearing site directory. Import a consistent logical database dump. **Do not migrate Plesk backup directories, plugin backup archives, old exports, logs or partial staging files.** Use exact exclusions so real media is not removed.
4. Verify the actual application's pages, login, forms, jobs and data on the new origin. Disable the old scheduler before enabling the new one.
5. Freeze source writes, copy the final delta, import the final database and switch the web records. Keep the old ingress forwarding during DNS convergence; never leave two writable versions.
6. Remove migration staging after the cutover and retain the source only for the agreed rollback window.

Container hosting does not replace mail hosting. Inventory email delivery, external storage, redirects and scheduled integrations per site rather than assuming a WordPress-only setup.

## ClickHouse

Applications retain the stable endpoint `https://clickhouse.fornace.net`. New app databases are created through Matchbox using the current shared `matchbox` skill, `references/app-databases.md`. Applications receive database-specific credentials; owner-wide credentials are not shared with them.

```sh
fornace-matchbox proxy clickhouse POST /databases -d '{"name":"my_app"}'
fornace-matchbox proxy clickhouse POST /databases/my_app/query -d '{"query":"SHOW TABLES"}'
```

Use `/link` to configure an authorized Vercel application without printing credentials. Do not change clients to a raw server IP or expose native ClickHouse ports publicly. Query, memory and storage budgets are separate controls. Keep business data unless the owning application's retention explicitly permits deletion; system query logs and temporary data have bounded retention.

## Backups and recovery

New encrypted backups go to the separate Hetzner Storage Box. Source backup history is not imported. The repository password and SSH recovery material must remain retrievable independently of the hosting box.

A successful backup includes live files and application-consistent database exports. Never back up an active database directory as though it were a consistent database snapshot. Restore using the installed backup tooling and recorded image versions, then verify the real application before directing traffic to it.

Both NVMe drives form one non-replicated volume. Losing either drive requires restoring onto repaired or replacement hardware; off-host backups provide recovery, not uninterrupted service.

## Keep the disk clean

- Container logs rotate at 10 MiB per file and three files per container. Journald is capped at 500 MiB.
- Temporary directories have size caps. Deployment releases retain the running and immediate prior revision only.
- Builds run elsewhere. Maintenance removes unused images and stale build cache, never active site data.
- Backup jobs serialize, preserve failure exit codes and remove their own partial staging files. Retention is seven daily, five weekly, six monthly and two yearly snapshots, subject to actual Storage Box capacity.
- Database binary logs and ClickHouse system logs have finite retention. No global TTL deletes customer tables.
- Watch disk, inode, per-site quota, backup age and NVMe health. A quota is not permission to fill all shared capacity simultaneously.

## Update this guide

Edit `docs/README.md` in `Fornace/fornace-hosting` and merge to `main`. The same workflow publishes the Markdown object to shared R2:

https://hosting-guide.fornace.app/README.md

Do not upload client inventory, connection credentials or private run logs to the public bucket.
