Docs

Getting Started

This page takes you from nothing to a running, health-checked Postgres with its connection string loaded in your shell. Budget about five minutes.

Prerequisites

  • Docker, installed and running. eph talks to your local Docker daemon to start containers. Confirm with docker ps.
  • A shell. The non-container features (run= services, lifecycle hooks, and shell health checks) run through the platform shell: sh -c on Linux and macOS, cmd /C on Windows. Everything works natively on all three platforms; the one catch is that a command string written for sh (pipes, $VAR, POSIX tools) may need a cmd-compatible form on Windows, or you can run eph inside WSL. See Troubleshooting.

Install

Install the latest release binary. The script verifies a SHA-256 checksum before installing:

# Linux / macOS
curl -sSfL https://raw.githubusercontent.com/attunehq/doteph/main/scripts/install.sh | bash
# Windows (PowerShell)
irm https://raw.githubusercontent.com/attunehq/doteph/main/scripts/install.ps1 | iex

Both scripts honor EPH_REPO (an owner/repo, default attunehq/doteph) and EPH_BASE_URL (replaces the GitHub download base entirely, with no release tag appended after it) for installing from a fork or a mirror; eph update honors the same two variables (see below).

Or build from a source checkout:

cargo install --path .
# or
make install

Confirm the binary is on your PATH:

eph --version

Keep it current later with the built-in updater: eph update installs the latest release (checksum-verified, swapped in place), and eph update --check just reports whether one exists. Details in the Command Reference.

Write your first .eph file

In the root of a project, create a file named .eph:

[postgres]
image=postgres:16-alpine
port=5432
env.POSTGRES_USER=dev
env.POSTGRES_PASSWORD=dev
env.POSTGRES_DB=myapp
healthcheck=pg_isready -U dev

[env]
DATABASE_URL=postgres://dev:dev@localhost:${postgres.port}/myapp

Reading it line by line:

  • [postgres] declares a service named postgres.
  • image= runs the official postgres:16-alpine Docker image.
  • port=5432 publishes the container’s port 5432 on a random free port on your machine, so it never collides with anything else.
  • env.POSTGRES_* are environment variables passed into the container; here they configure the Postgres superuser and database.
  • healthcheck= is a command eph runs repeatedly until it succeeds, so eph up only returns once Postgres actually accepts connections.
  • [env] opens a section for top-level variables: a plain KEY=VALUE line directly after a service section is a parse error (see The .eph File), so anything you want eph env to export after your services go here.
  • DATABASE_URL=... is a top-level environment variable for your shell. ${postgres.port} is replaced with the real assigned host port when you run eph env.

Comments must be on their own line, starting with #. A # after a value is part of the value, not a comment. See The .eph File.

Validate it

Before starting anything, check that the file parses:

eph check

This reports the environment variables and services it found, or a parse error with a line number. It never touches Docker, so it is always safe to run.

Start your services

eph up

eph pulls the image if needed, starts the container, waits for the health check to pass, and prints the assigned port:

Services started:
  postgres -> localhost:54321

Run `eval "$(eph env)"` to set environment variables

See what is running

eph status
Workspace: /home/you/projects/myapp
ID: a1b2c3d4e5f60718

Running services:
  postgres -> localhost:54321

Load the connection details into your shell

eph env prints shell-ready variable assignments with the real ports filled in:

$ eph env
export DATABASE_URL="postgres://dev:dev@localhost:54321/myapp"

Load them into your current shell:

eval "$(eph env)"              # bash / zsh / sh

$DATABASE_URL points at your running Postgres, and your app can connect. fish, PowerShell, and JSON formats are covered in Shell Integration.

Stop your services

eph down

This stops the container but keeps it and its data, so the next eph up is fast. To also remove the container:

eph down --rm

To wipe everything for this workspace (containers, named volumes and their data, and saved state):

eph clean

The core loop

That is the whole day-to-day workflow:

eph up                 # start services
eval "$(eph env)"      # load connection details
# ... work ...
eph down               # stop when you are done

Next

You have the mechanics. Read Core Concepts to understand why it works this way (workspaces, isolation, ports, and the lifecycle), which makes everything else in the guide fall into place.