Skip to main content
Feather deploys as a Docker image behind Caddy, on one machine. A €7/month VPS runs the app, Postgres, Redis and TLS termination with room to spare, and the whole thing is eight files in your repository. feather new scaffolds them. To add them to an existing app:
It inspects your project to decide what to generate — a db service if you have a database, redis and a worker if you use background jobs — and never overwrites a file that already exists unless you pass --force.

What gets generated

The web target is last in the Dockerfile, so a plain docker build . produces the web image. The worker is docker build --target worker .. Both come from one Dockerfile and share a layer cache.

Two details that are easy to break

The frontend stage copies the framework’s templates out of that layer. Tailwind scans them for the class names the built-in components use.Remove that copy and every framework component renders unstyled in production.
Two web containers starting at once would race on feather db upgrade. deploy/deploy.sh runs them exactly once, in a throwaway container, before any long-lived container starts.

Local development

The app itself stays on your machine so feather dev keeps Vite’s hot reload. Only the dependencies go in containers.
The ports and credentials match the DATABASE_URL and REDIS_URL in the generated .env, so nothing else needs configuring. Bring up one service alone with docker compose -f docker-compose.dev.yml up -d db. Stop them with down, and add -v to throw the local data away. You can still build and run the production image locally to check it:

Next: first deploy to a VPS

DNS, a deploy user, .env on the server, and the deploy script.