# 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/` and a direct `/apps//` 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).