conductor/docs/INSTALL.md
Victor Wiebe 498003f83d
Some checks failed
Release production image / production-image (push) Has been cancelled
Prepare Conductor v1.0.0 release
2026-08-08 14:21:40 -04:00

241 lines
9.7 KiB
Markdown

# Install Conductor on Your Own Server
This guide is for independent self-hosters installing Conductor on an ordinary internet-connected server. It uses the public source bundle, public container image, Docker Compose, and a reverse proxy you control. No SkeletonWorks tooling or registry account is required.
The recommended path runs the published container. The tagged source bundle supplies the matching Compose file, environment template, backup/restore scripts, and documentation.
Use the `v1.0.0` commands below only after the public Gitea Release entry exists. Before publication, do not substitute an older release candidate and treat a missing source tag or image as an incomplete release rather than an installation problem.
## What you need
- A GNU/Linux `x86_64`/AMD64 server. Conductor v1.0.0 is not published for ARM64.
- Docker Engine with the `docker compose` plugin. Use Docker's [Engine installation guide](https://docs.docker.com/engine/install/) and [Compose plugin guide](https://docs.docker.com/compose/install/linux/) if needed.
- Permission to run Docker commands.
- `curl`, `openssl`, `tar`, `bash`, `realpath`, `find`, `sha256sum`, and GNU `stat`.
- About 1 GiB of memory and 1.5 CPU available with the default limits.
- A domain name whose DNS points to the server.
- Inbound ports 80 and 443 for TLS, and outbound HTTPS access for image pulls and REST destinations.
- Caddy, NGINX, or another reverse proxy on the same host.
These commands assume `docker version` and `docker compose version` succeed without `sudo`. If your Docker installation requires elevated access, apply your normal server policy consistently.
Confirm the architecture before continuing:
```bash
uname -m
```
Continue only if it prints `x86_64` or the equivalent AMD64 name for this release.
## 1. Download the pinned installation bundle
Choose a directory that will remain on the server. The example uses the current user's home directory:
```bash
mkdir -p "$HOME/conductor"
cd "$HOME/conductor"
CONDUCTOR_RELEASE=v1.0.0
curl --fail --location --show-error \
"https://gitea.skeletonworks.online/vwiebe/conductor/archive/${CONDUCTOR_RELEASE}.tar.gz" \
--output conductor-source.tar.gz
tar -xzf conductor-source.tar.gz --strip-components=1
rm conductor-source.tar.gz
```
The `v1.0.0` source tag and the container image's OCI `revision` label must identify the same release commit. The authoritative commit, archive checksums, and image digests are recorded in the Gitea `v1.0.0` Release entry after publication. Keep `compose.production.yml`, `.env`, and `scripts/production/` together so the documented backup and restore commands work.
## 2. Create the persistent configuration
Run this block once. If `.env` already exists from an earlier installation, stop and keep it; overwriting its encryption key can make stored credentials unreadable.
```bash
cd "$HOME/conductor"
test ! -e .env || { echo '.env already exists; leaving it unchanged.' >&2; exit 1; }
umask 077
secret_key="$(openssl rand -hex 32)"
session_key="$(openssl rand -hex 32)"
{
printf 'CONDUCTOR_SECRET_KEY=%s\n' "$secret_key"
printf 'CONDUCTOR_SESSION_KEY=%s\n' "$session_key"
printf 'CONDUCTOR_IMAGE=gitea.skeletonworks.online/vwiebe/conductor\n'
printf 'CONDUCTOR_VERSION=v1.0.0\n'
printf 'CONDUCTOR_BIND_ADDRESS=127.0.0.1\n'
printf 'CONDUCTOR_PORT=8080\n'
printf 'CONDUCTOR_MEM_LIMIT=1g\n'
printf 'CONDUCTOR_CPUS=1.5\n'
printf 'CONDUCTOR_PROXY_INTERNAL_ORIGINS=\n'
} > .env
unset secret_key session_key
chmod 600 .env
```
`CONDUCTOR_SECRET_KEY` encrypts stored REST credentials. The database and this exact key are one recovery set. `CONDUCTOR_SESSION_KEY` protects login sessions; changing it signs everyone out. Never commit `.env`, paste its values into tickets, or include them in screenshots.
The loopback bind keeps port 8080 off the public network. Only the reverse proxy should accept internet traffic.
## 3. Pull and start Conductor
```bash
cd "$HOME/conductor"
docker compose --env-file .env -f compose.production.yml pull
docker compose --env-file .env -f compose.production.yml up -d
for _ in $(seq 1 60); do
if curl --fail --silent http://127.0.0.1:8080/api/health >/dev/null; then
break
fi
sleep 2
done
docker compose --env-file .env -f compose.production.yml ps
curl --fail --show-error http://127.0.0.1:8080/api/health
```
The final command should return healthy JSON. If it fails, inspect:
```bash
docker compose --env-file .env -f compose.production.yml logs --tail=200 conductor
```
The v1.0.0 release is complete only after this pull succeeds without a Gitea username, password, or token. Check the Gitea `v1.0.0` Release entry for the published OCI index and AMD64 manifest digests. Do not substitute `latest` or an older release-candidate digest.
## 4. Configure HTTPS
Production login and setup cookies are `Secure`, so finish HTTPS before creating the first administrator. Plain HTTP is suitable for the loopback health check, not for normal browser use.
Choose one reverse proxy. The examples assume the proxy runs directly on the Docker host. Replace `conductor.example.com` with your real hostname and create its DNS record first.
### Caddy
Install Caddy using its [official installation guide](https://caddyserver.com/docs/install). Add this site block to `/etc/caddy/Caddyfile`:
```caddyfile
conductor.example.com {
reverse_proxy 127.0.0.1:8080
encode zstd gzip
}
```
Then validate and reload it:
```bash
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```
Caddy obtains and renews TLS certificates automatically when DNS and inbound ports 80/443 are correct.
### NGINX
Provision a valid TLS certificate using your normal NGINX process, then proxy every path to Conductor:
```nginx
server {
listen 443 ssl;
server_name conductor.example.com;
# Configure ssl_certificate and ssl_certificate_key for this hostname.
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
Validate and reload NGINX after placing the block in the configuration used by your distribution. Configure the port-80 server to redirect HTTP to HTTPS.
If the reverse proxy runs on another host, do not expose port 8080 to the whole internet. Bind it to a private interface and restrict that port to the proxy host with network and firewall rules.
## 5. Create the first administrator
Open the final URL, for example:
```text
https://conductor.example.com
```
The first-run screen asks for a username and a password of at least 12 characters. Display name and contact email are optional. Conductor creates that administrator and closes first-run setup permanently. There are no default credentials.
Create a second administrator from **Users** before relying on the installation. Application administration is covered in the [Administrator Guide](ADMIN_GUIDE.md).
## 6. Smoke-test persistence and routing
1. Sign in and create a small project in **Visual Editor**.
2. Save it, restart the container, and sign in again:
```bash
cd "$HOME/conductor"
docker compose --env-file .env -f compose.production.yml restart conductor
```
3. Load the saved project and confirm it persisted.
4. Publish it and open both `/apps/<app-slug>` and a direct `/apps/<app-slug>/<page-slug>` URL in new browser tabs.
The editor, API, and published routes should all use the same HTTPS origin. A direct deep link must not return the reverse proxy's 404 page.
## Internal REST destinations
Conductor allows public HTTP/HTTPS destinations that pass its proxy policy and denies private or internal networks by default. If an application must call an internal API, add only its reviewed exact origin to `.env`, for example:
```dotenv
CONDUCTOR_PROXY_INTERNAL_ORIGINS=https://api.internal.example.com:443
```
Multiple origins are comma-separated. Wildcards, URL paths, credentials, and IP literals are not accepted. Apply a configuration change with:
```bash
docker compose --env-file .env -f compose.production.yml up -d
```
Read [Proxy Security](PROXY_SECURITY.md) before adding an exception.
## Back up before real use
The installation bundle includes the application-owned online backup script:
```bash
cd "$HOME/conductor"
scripts/production/backup-conductor.sh \
--compose-file compose.production.yml \
--env-file .env \
--backup-dir ./backups \
--retention-days 7
```
Copy the resulting archive off the server and protect it like a password; it includes the database and recovery keys. Follow [Production Operations](OPERATIONS.md) for verified restore, upgrade, rollback, health, and log procedures.
## Build from source instead
The downloaded tag is a complete source tree. To build the production image locally rather than pull the public image, create `.env` as above and run:
```bash
docker compose --env-file .env \
-f compose.production.yml \
-f compose.production.build.yml \
up -d --build
```
This multi-stage build downloads Node packages, compiles the React frontend and TypeScript backend, and runs the same non-root production service. AMD64 is the validated release architecture; an ARM64 source build is not yet a supported release path.
Do not use `docker-compose.yml` for production. It is the two-container development environment.
## Stop or uninstall
Stop and remove the application container while preserving its named data volume:
```bash
docker compose --env-file .env -f compose.production.yml down
```
Do not add `--volumes` unless permanent data deletion is intentional and a verified off-host backup exists. Retain `.env` with the backup set even after removing the container.
For common failures, see [Troubleshooting](TROUBLESHOOTING.md).