A self-hosted installation runs the Ephemeral engine and execution dashboard on a host you control, with workers in microVMs in your own AWS account and your own model provider. You receive one Docker image and its documentation; the image exports every other file you need. It requires a deployment license from Ephemeral.
What you run
| Part | Where |
|---|---|
| Engine API and execution dashboard | Container app from ephemer.al/ephemeral/self-hosted on your host |
| PostgreSQL | A container on the same host, reachable only from the application |
| Caddy | Serves HTTPS on ports 80 and 443 and routes /v1 to the engine |
| Workers | AWS Lambda microVMs in your AWS account, one per run, with no internet access |
| Model calls | Your OpenAI-compatible inference endpoint |
| License validation | https://ephemer.al, at startup and while runs start |
The customer website, accounts and billing are not part of a self-hosted installation, and neither is source code. People sign in to the dashboard with an engine API key.
The image contains the complete operator guide. The steps below summarize it; follow the guide for exact commands, expected output, upgrades, backups and troubleshooting:
docker run --rm ephemer.al/ephemeral/self-hosted:VERSION setup docs > SELF-HOSTING.mdBefore you start
- Docker with Docker Compose 2.24 or newer, and AWS CLI v2 with the Session Manager plugin.
- An AWS account in a region with Lambda microVMs, and permission to create a CloudFormation stack with IAM roles.
- A domain name you can point at the host.
- An OpenAI-compatible inference endpoint and API key.
- An entitlement for your organization. Ephemeral grants it after an agreement; see deployment licenses.
Install
Register the installation
In the customer portal, open Deployments and register this installation with a name, such as
production-eu. Ephemeral creates its machine ID and license token. Choose Download license file to keep the token, the machine ID and the deployment ID; the token is shown once.Pull the image
Sign in to the registry with the deployment ID as user name and the license token as password, then pull a release. Versions have no leading v.
export EPHEMERAL_VERSION=0.1.0 read -rs LICENSE_TOKEN printf '%s' "$LICENSE_TOKEN" | docker login ephemer.al -u dep_YOUR_DEPLOYMENT_ID --password-stdin docker pull ephemer.al/ephemeral/self-hosted:$EPHEMERAL_VERSIONExport the installation files
mkdir -p ephemeral && cd ephemeral docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/out" \ ephemer.al/ephemeral/self-hosted:$EPHEMERAL_VERSION setup all /outFile Purpose compose.yaml PostgreSQL, the application and Caddy, pinned to this release compose.evaluation. yaml Override for trying it on one computer without AWS CaddyfileIngress routes .env Configuration with newly generated secrets. A later export never overwrites it ephemeral-aws.yaml CloudFormation template for the AWS resources ephemeral-worker-VERSION.zip The compiled worker that AWS builds into the microVM image SELF-HOSTING.md The operator guide Create the AWS resources
Upload the worker ZIP to a private S3 bucket in your account, then create the stack. It creates a VPC with an isolated worker subnet, a DNS firewall, the microVM image and network connector, an encrypted storage bucket, the host's least-privilege IAM role and, by default, a
t4g.smallAmazon Linux host with an Elastic IP and an encrypted data volume. Building the worker image takes 15 to 30 minutes, and the stack creates billable resources.aws s3 cp "ephemeral-worker-$EPHEMERAL_VERSION.zip" "s3://YOUR_ARTIFACT_BUCKET/" aws cloudformation create-stack --stack-name ephemeral \ --template-body file://ephemeral-aws.yaml --capabilities CAPABILITY_IAM \ --parameters ParameterKey=Domain,ParameterValue=ephemeral.example.com \ ParameterKey=WorkerArtifactBucket,ParameterValue=YOUR_ARTIFACT_BUCKET \ ParameterKey=WorkerArtifactKey,ParameterValue="ephemeral-worker-$EPHEMERAL_VERSION.zip" \ ParameterKey=HostImageId,ParameterValue=AL2023_ARM64_AMI aws cloudformation wait stack-create-complete --stack-name ephemeral aws cloudformation describe-stacks --stack-name ephemeral \ --query "Stacks[0].Outputs[?OutputKey=='EngineEnvironment'].OutputValue" --output text | tr ' ' '\n'The last command prints the AWS settings for the engine. Point your domain's DNS A record at the stack output
HostPublicIp.Configure the host
Connect with
aws ssm start-session --targetand the stack outputHostInstanceId, then repeat the registry sign-in and the export in/srv/ephemeral/install. Edit these entries in.env:Setting Value EPHEMERAL_DOMAIN Your hostname, such as ephemeral.example.com EPHEMERAL_LICENSE_ TOKEN The token from the first step EPHEMERAL_MACHINE_ ID The machine ID from the first step, exactly as registered INFERENCE_BASE_ URL Your OpenAI-compatible endpoint INFERENCE_API_ KEY Its API key EPHEMERAL_DEFAULT_ MODEL The model runs use when they do not name one AWS settings The five lines the stack printed: AWS_REGION,EPHEMERAL_S3_BUCKETand threeEPHEMERAL_MICROVM_…valuesThe export generated the encryption master key, the bootstrap admin key and the database password. Store a copy of
.envin your secret manager; without the master key, stored credentials and run payloads cannot be read. The host's instance profile supplies AWS credentials.Start the installation
Caddy obtains the HTTPS certificate. PostgreSQL and the internal APIs are not published.
docker compose pull docker compose up -dThe engine validates the license at startup and refuses to start if it is invalid or if the configured concurrency exceeds it.
Create the first organization
A new installation has no API keys. Run this once in the installation directory, replacing
my-organizationwith an organization ID of your choice. It reads the bootstrap key inside the container, so the key stays out of your shell history.docker compose exec -T app node --input-type=module - <<'NODE' if (process.env.EPHEMERAL_MODE !== 'self-hosted') { throw new Error('Bootstrap requires EPHEMERAL_MODE=self-hosted'); } const adminKey = process.env.EPHEMERAL_ADMIN_KEY; if (!adminKey) throw new Error('EPHEMERAL_ADMIN_KEY is not configured'); const response = await fetch('http://127.0.0.1:8080/admin/organizations', { method: 'POST', headers: { Authorization: `Bearer ${adminKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({id: 'my-organization'}), }); const result = await response.json(); if (!response.ok) throw new Error(result.error || `Bootstrap failed (${response.status})`); console.log(JSON.stringify(result, null, 2)); NODEThe response contains the organization
id, the new owner key'skey_idandrole, and thekeyitself, starting witheph_. Store the key now; it cannot be retrieved later. Running the command again for an organization that has an active key returns409.Sign in
Open your HTTPS address and enter the key under Engine API key. Create further member and admin keys under Keys. The bootstrap admin key from the configuration file only creates organizations; it cannot sign in or call the API.
Try it on one computer
Without AWS, add the evaluation override. Agents then run inside the application container with the local runner, and the dashboard is served over plain HTTP at http://localhost:8000 only. A license is still required; register a separate deployment for it.
docker compose -f compose.yaml -f compose.evaluation.yaml up -dDifferences from the hosted service
| Self-hosted behavior | |
|---|---|
| Billing | No wallet and no charges from Ephemeral. max_cost_micros is validated but does not limit model spending, so set limits with your provider. |
| Models | Runs can name any model ID your endpoint accepts. The catalog lists only the default. |
| API keys | Keys start with eph_. Admin keys manage keys through /v1/keys and the dashboard. |
| Accounts | There are no user accounts, members or invitations. Access is by API key. |
| Concurrency | Two runs at a time by default. Set EPHEMERAL_CONCURRENCY from 1 to 32, within your license. |
| Retention | Finished runs are kept 30 days. Set EPHEMERAL_RETENTION_DAYS to change it. |
| Zero data retention | Runs with zero_data_retention keep their events and unread output only in unlogged tables, deleted minutes after the run. Backups must use pg_dump --no-unlogged-table-data; the host's daily backup does. EPHEMERAL_ZDR_EVENT_WINDOW_SECONDS (default 300) and EPHEMERAL_ZDR_OUTPUT_WINDOW_SECONDS (default 3600) set how long they may wait. |
Updates and backups
To upgrade, upload the new release's worker ZIP and update the stack with its key. Then, on the host, export the new release's files, set EPHEMERAL_VERSION and the new EPHEMERAL_MICROVM_IMAGE_VERSION in .env, and run docker compose up -d. Runs in progress get up to five minutes to finish; runs still active after that are marked interrupted and are not retried, so external actions are never repeated. Database migrations run automatically.
Back up both the database and the encryption master key. Losing the master key makes stored connection credentials and run payloads unreadable. The host created by the stack backs up the database to its S3 bucket every day.