README.md
Rama main · réplica de lectura del repositorio original.
# siteio
A self-hosted deployment platform for static websites and Docker containers with automatic HTTPS via Traefik.
## Installation
```bash
# macOS / Linux (official installer)
curl -LsSf https://houlahop.com/siteio/install | sh
# Or from this repo with Bun
bun install
bun run src/cli.ts --help
```
Windows binaries are not shipped in releases yet.
## Quick Start
Configure your client to connect to the siteio agent server:
```bash
siteio login --api-url https://api.yourdomain.com --api-key your-api-key
```
Or use a connection token:
```bash
siteio login --token <connection-token>
```
---
## Provision a server
Create a fresh Hetzner VM from your laptop, install the agent, and log in:
```bash
siteio config set hetznerToken <hetzner-cloud-token>
siteio server create mybox --sslip --email you@yourdomain.com --identity ~/.ssh/id_ed25519
```
Without `--sslip`, a configured Cloudflare token enables an interactive zone picker
with **Buy a new domain** as the last option:
```bash
siteio config set cloudflareToken <cloudflare-api-token>
siteio server create mybox
# Or use an existing zone without prompts:
siteio server create mybox --domain yourdomain.com --email you@yourdomain.com --identity ~/.ssh/id_ed25519
```
`HETZNER_TOKEN` (also `HCLOUD_TOKEN`) and `CLOUDFLARE_API_TOKEN` (also
`CLOUDFLARE_TOKEN`) override saved credentials. Config files are written with mode
0600 and tokens are masked by `config get` and `config set`, including JSON output.
The SSH public key must be next to its private key as `<identity>.pub`; multiple
keys in `~/.ssh` trigger a picker unless `--identity` is given.
By default the command resolves the cheapest currently available x86 VM type from
Hetzner's API in Falkenstein (`fsn1`) and uses the `docker-ce` app image. Override
with `--type <type>` and `--location <location>`. The VM is billable and its firewall
allows inbound TCP 22, 80 and 443. Custom wildcard DNS points directly to the VM.
Existing DNS with different settings is rejected; matching pre-existing records
are preserved during destruction.
Cloudflare requires Zone Read and DNS Edit permissions; buying also needs Registrar
Write and Account Settings Read for account discovery. Verify these permissions in
the dashboard when creating the token; the Registrar template key still needs live
dashboard validation. Purchases display the domain,
registration price and renewal price and require an explicit, non-refundable purchase
confirmation. Set up the account's payment method, default registrant contact and
Domain Registration Agreement in the Cloudflare dashboard first. API-supported TLDs
are checked at runtime; premium domains are not purchased. Auto-renew defaults to off.
See the [Cloudflare Registrar workflow](https://developers.cloudflare.com/registrar/registrar-api/).
Progress is saved in `~/.config/siteio/provisioned-servers/<name>.json` after each
step. Run `siteio server create mybox` again to resume. Omitted names default to
`siteio`. Keep the saved files: they identify resources owned by this command.
A pending or uncertain purchase is checked by domain status and is never submitted
again automatically. If Cloudflare reports a failed, blocked or action-required
registration, resolve it in the dashboard before resuming. DNS propagation, VM boot
and certificate issuance may require another run after a timeout.
```bash
siteio server destroy mybox
# For scripts, explicitly confirm destruction:
siteio server destroy mybox --yes
```
Destroy removes the VM, firewall, owned wildcard DNS and local login entry. It keeps
the domain registration and uploaded SSH key. Failed cleanup can be resumed with the
same command. `server` manages cloud infrastructure; `agent` manages software on a box.
---
## Try It Out
This repo includes working examples you can deploy immediately:
```bash
# Deploy the example static site
siteio sites deploy examples/static -n hello
# Deploy a pre-built Docker image
siteio apps create nginx-demo --image nginx:alpine --port 80
siteio apps deploy nginx-demo
# Build and deploy from Git (this repo!)
siteio apps create docker-demo \
--git https://github.com/plosson/siteio \
--context examples/docker \
--port 3000
siteio apps deploy docker-demo
```
After deployment:
- Static site: `https://hello.yourdomain.com`
- Nginx demo: `https://nginx-demo.yourdomain.com`
- Docker demo: `https://docker-demo.yourdomain.com`
---
## Site Examples
### Example 1: Deploy a Website
Deploy a folder as a website:
```bash
siteio sites deploy ./my-website -n blog
```
This deploys your files to `https://blog.yourdomain.com`.
### Example 2: A Site with Auth, Database, and Storage
Every site ships with a PocketBase backend at `/api` — auth, database, file
storage, and realtime, no server code required:
```bash
siteio sites init ./todo # scaffold: index.html + starter schema + AI guide
siteio sites dev # run locally with the backend, no Docker
siteio sites deploy # ship it
siteio sites admin # backend dashboard URL + credentials
```
---
## Docker Container Examples
### Example 1: Simple Nginx Web Server
Deploy a basic Nginx container:
```bash
# Create the app
siteio apps create mysite --image nginx:alpine --port 80
# Deploy it
siteio apps deploy mysite
```
Available at `https://mysite.yourdomain.com`.
### Example 2: Deploy from Git Repository
Build and deploy directly from a Git repository:
```bash
# Create app from GitHub repo
siteio apps create myapi --git https://github.com/user/myapi --port 3000
# Deploy (clones repo, builds image, runs container)
siteio apps deploy myapi
```
With custom options:
```bash
# Specify branch and Dockerfile
siteio apps create myapi --git https://github.com/user/myapi \
--branch develop \
--dockerfile Dockerfile.prod \
--port 3000
# Force rebuild without Docker cache
siteio apps deploy myapi --no-cache
```
#### Auto-deploy
A git app can redeploy itself. The agent checks the repository every 5 minutes;
nothing has to change in the repository.
```bash
# Redeploy on each new commit of the tracked branch
siteio apps set myapi --auto-deploy commit
# Redeploy on each higher release tag (vMAJOR.MINOR.PATCH only; v1.2.3-rc1 is ignored)
siteio apps set myapi --auto-deploy tag
# Stop
siteio apps set myapi --auto-deploy off
```
- In `tag` mode the tag itself is built, and an older tag never causes a downgrade.
- A manual `siteio apps deploy` still builds the branch HEAD. Use it to deploy a pre-release.
- A failed build leaves the running version up and is not retried until a newer
commit or tag appears. `siteio apps info` shows the error.
- Compose apps are not supported.
- Set the interval with `SITEIO_AUTODEPLOY_INTERVAL` (seconds, default 300, minimum 60) on the agent, or with `autoDeployInterval` in the agent's `agent-config.json`. The env var wins when set and not empty.
### Example 3: Monorepo Deployment
Deploy a service from a monorepo by specifying the build context:
```bash
# Deploy backend service from monorepo
siteio apps create backend --git https://github.com/user/monorepo \
--context services/backend \
--dockerfile Dockerfile \
--port 8080
siteio apps deploy backend
```
### Example 4: Node.js API with Environment Variables
Deploy a Node.js application with configuration:
```bash
# Create the app
siteio apps create nodeapi --image node:20-alpine --port 3000
# Configure environment variables
siteio apps set nodeapi \
-e NODE_ENV=production \
-e DATABASE_URL="postgres://user:pass@db.example.com:5432/mydb"
# Configure secrets (stored encrypted, never displayed again)
siteio apps set nodeapi --secret JWT_SECRET="your-secret-key"
# Set restart policy
siteio apps set nodeapi -r unless-stopped
# Deploy
siteio apps deploy nodeapi
```
### Secret environment variables
`-e KEY=value` is plain configuration: it is stored as-is and printed back by
`siteio apps info`. For anything sensitive — a passphrase, a database URL, an
API token, a signing key — use `--secret` instead:
```bash
# Inline
siteio apps set myapp --secret VAULT_PASSPHRASE="correct horse battery staple"
# From a file, keeping the value out of shell history and the process list
siteio apps set myapp --secret-file VAULT_PASSPHRASE=./passphrase.txt
# From stdin, same idea
pass show vault | siteio apps set myapp --secret-stdin VAULT_PASSPHRASE
# A whole env file at once — every key in it becomes a secret
siteio apps set myapp --secret ./secrets.env
```
The agent stops sending the value back once a key is marked secret, so the CLI
has nothing to print:
```console
$ siteio apps info myapp
Environment:
NODE_ENV=production
VAULT_PASSPHRASE=•••••••• (secret)
```
There is no flag to reveal it — a secret is write-only once set. To change one,
set it again; to remove it, `siteio apps unset myapp -e VAULT_PASSPHRASE`.
Restart the app for either to take effect.
What this is for: stopping the CLI from being the easy way to read every secret
of every app, and keeping values off your screen during a share or a demo. It
is not protection against someone with access to the server — the agent stores
the value as it stores any other env var, and the container needs it in its
environment, so `docker inspect` on the host still shows it.
### Example 5: PostgreSQL Database with Persistent Storage
Deploy PostgreSQL with a persistent volume:
```bash
# Create the app
siteio apps create postgres --image postgres:16-alpine --port 5432
# Configure with environment and volume
siteio apps set postgres \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_DB=myapp \
-v pgdata:/var/lib/postgresql/data \
-r always
# Deploy
siteio apps deploy postgres
```
### Example 6: Redis Cache with Custom Domain
Deploy Redis with a custom domain:
```bash
# Create the app
siteio apps create redis --image redis:7-alpine --port 6379
# Configure volume and custom domain
siteio apps set redis \
-v redis-data:/data \
-d cache.example.com \
-r unless-stopped
# Deploy
siteio apps deploy redis
```
### Example 7: Full-Stack Application (Multiple Services)
Deploy a complete application stack:
```bash
# Backend API
siteio apps create backend --image myregistry/backend:latest --port 8080
siteio apps set backend \
-e NODE_ENV=production \
-e DATABASE_URL="postgres://user:pass@postgres.yourdomain.com:5432/app" \
-e REDIS_URL="redis://redis.yourdomain.com:6379" \
-v uploads:/app/uploads \
-d api.example.com \
-r unless-stopped
siteio apps deploy backend
# Background worker
siteio apps create worker --image myregistry/worker:latest --port 9000
siteio apps set worker \
-e NODE_ENV=production \
-e DATABASE_URL="postgres://user:pass@postgres.yourdomain.com:5432/app" \
-e REDIS_URL="redis://redis.yourdomain.com:6379" \
-r unless-stopped
siteio apps deploy worker
```
---
## Common Commands
```bash
# List all sites
siteio sites list
# List all apps
siteio apps list
# View app logs
siteio apps logs myapp --tail 100
# Stop an app
siteio apps stop myapp
# Restart an app
siteio apps restart myapp
# Bring a stopped app back (same image, no rebuild)
siteio apps start myapp
# Take a site offline (404) and bring it back
siteio sites stop mysite
siteio sites start mysite
# Remove an app
siteio apps rm myapp
# Remove a site
siteio sites rm mysite
# Open the in-site live editor: chat with an AI editor on the site itself and
# watch changes land in place (requires an LLM credential configured on the agent)
siteio sites edit mysite --open
# Revoke all outstanding editor links for a site
siteio sites edit mysite --revoke
```
## JSON Output
All commands support `--json` for scripting:
```bash
siteio --json sites list
siteio --json apps info myapp
```