Frameworks — zeltro new
zeltro new <framework> <name> scaffolds a greenfield project you write. Both arguments are required positionals.
zeltro new laravel my-shop
zeltro new flask my-api --database sqlite
zeltro new express my-service --database postgres --version latest
For ready-made third-party apps you run rather than write, see App library instead.
Supported frameworks#
| Framework | Runtime image | Default database |
|---|---|---|
laravel |
PHP 8.3 | MySQL |
kavera |
PHP 8.3 | MySQL |
octobercms |
PHP 8.3 | MySQL |
drupal |
PHP 8.3 | MySQL |
wordpress |
PHP 8.3 | MySQL |
php |
PHP 8.3 | MySQL |
fastapi |
Python 3.12 | PostgreSQL |
flask |
Python 3.12 | PostgreSQL |
django |
Python 3.12 | PostgreSQL |
python |
Python 3.12 | PostgreSQL |
express |
Node 22 | MySQL |
nestjs |
Node 22 | MySQL |
fastify |
Node 22 | MySQL |
node |
Node 22 | MySQL |
nextjs |
Node 22 | SQLite |
nuxt |
Node 22 | SQLite |
sveltekit |
Node 22 | SQLite |
astro |
Node 22 | SQLite |
hono |
Node 22 | SQLite |
react |
Node 22 | SQLite |
vue |
Node 22 | SQLite |
Each runtime is one of Zeltro's base images: canebaycomputers/cbc:nginx-php8 (PHP 8.3 behind nginx and php-fpm), canebaycomputers/cbc:nginx-python3 (Python 3.12; your app listens on port 8000 and nginx proxies it) or canebaycomputers/cbc:nginx-node (Node 22; your app listens on port 3000 and nginx proxies it). Every project answers on port 80.
If you ask for a database the framework can't use, Zeltro warns and switches to one it can. WordPress is MySQL only, and Laravel, Kavera, October CMS, Drupal and Django don't get MongoDB.
Front-end frameworks#
nextjs, nuxt, sveltekit, astro, react and vue are scaffolded as
running dev servers with hot reload, proxied through nginx on port 80. Edit a
file and the page updates — no build step, no port to remember.
They default to SQLite, unlike every other framework here, because a front-end project should not start a database server it never queries. Ask for one explicitly when you need it:
zeltro new nextjs my-app --database postgres
react and vue are plain single-page apps on Vite with no server rendering.
hono is an API framework rather than a UI one, and is grouped with them only
because it shares the same Node base image.
Hot reload is wired for you. The dev server's own port is never published.
The browser reaches the app on port 80 through nginx, so the Vite-based
projects (nuxt, sveltekit, astro, react, vue) pin the HMR socket to
port 80 in their config (server.ws.clientPort, which Vite 8.1 renamed from
server.hmr.clientPort). Change that and hot reload stops connecting while
the page still loads, which is a confusing failure. nextjs needs no port
setting, because its dev server connects back through the same address as
the page. Next.js 16 does refuse hot-reload connections from hosts it doesn't
know, so the scaffold's next.config.mjs lists the private IPv4 ranges in
allowedDevOrigins. Add any other hostname you browse the project by.
Kavera#
Kavera is a Laravel-native website framework: flat-file Blade pages plus service-driven dynamic content (Blogger posts, Eventbrite events, Flickr galleries, form webhooks) cached through Redis. Forms ship with email, webhooks, spam controls and reCAPTCHA; pages carry SEO titles and optional JSON-LD.
It exists because an agent editing page files beats an agent driving a CMS admin UI. Reach for it over plain Laravel for marketing sites, brochure sites, portfolios and galleries — and for plain Laravel when you need a real application with custom models and business logic.
zeltro new kavera my-site
Pages live in resources/views/content. After adding or removing one, refresh the registry so routes resolve:
zeltro art app:update-content-list
October CMS and Drupal#
Both are CMSs with an admin UI, installed from source so they use Zeltro's shared databases.
- October CMS (
zeltro new octobercms my-site) is Laravel-based. Themes and plugins live in the project, so an agent can edit them. The admin is at/admin(theBACKEND_URIin.env); create the admin user withzeltro art october:passwd <email> <password>. It is free for local development, but production use needs a licence from octobercms.com. - Drupal (
zeltro new drupal my-site) installs Drupal 11 with Composer, then runsdrush site:install. The docroot ispublic/instead of Drupal's usualweb/. It is the slowest framework to create, so expect several minutes. Run Drush withzeltro drush <args>.
Options#
| Option | Description | Values |
|---|---|---|
--database <type> |
Database engine | auto (default: see the table above), mysql, postgres, mongodb, sqlite |
--db-name <name> |
Database name | Default: project name, dashes → underscores |
--version <ver> |
Framework version | Laravel and WordPress only: latest (default) or a version number. Other frameworks ignore it |
--image <ref> |
Override the Docker image | Default: the framework's base image |
--no-migration |
Skip migrations | Migrations run by default |
--one-off |
Skip the AI hand-off after creation | |
--github |
Create a GitHub repo in your account | Requires gh auth |
--github-org <org> |
Create the repo in an organization | |
--public / --private |
Repo visibility | Default private |
--no-storage-symlink |
Skip public/storage symlink |
Laravel only |
Databases#
SQLite#
zeltro new flask notes --database sqlite
zeltro new django blog --database sqlite
zeltro new laravel shop --database sqlite
SQLite needs no shared service — it's a single file. Zeltro creates it, points the project's .env at it, and runs migrations normally.
The file always lives inside the project directory:
| Framework | Path |
|---|---|
| Django | db.sqlite3 |
| Laravel, Kavera, October CMS | database/database.sqlite |
| Drupal | public/sites/default/files/.ht.sqlite |
| Everything else | database.sqlite |
That location is deliberate. The project directory is the only path bind-mounted into the container, so a database anywhere else would be destroyed every time the container is recreated on zeltro up. It is also gitignored by default.
Good for prototypes, single-user tools, and test fixtures. For anything concurrent, use Postgres or MySQL.
Shared server databases#
mysql, postgres and mongodb connect to the shared service containers. Zeltro creates the database and writes the connection settings into the project's .env — you never configure credentials by hand. See Architecture → Shared services for hostnames and credentials.
Working inside a project#
Run these from the project directory. They execute inside the container, with the correct runtime.
PHP#
zeltro composer install
zeltro art migrate
zeltro wp plugin list --status=active
zeltro drush status
zeltro php script.php
zeltro tinker
Python#
zeltro python -c "import sys; print(sys.version)"
zeltro pip install httpx
zeltro django manage migrate
zeltro django manage createsuperuser
zeltro shell
Python containers provide python3, not python.
Node#
zeltro npm install
zeltro npx tsc --init
zeltro node script.js
zeltro shell
Any framework#
zeltro exec <cmd> # run a command, no TTY — good for scripts and CI
zeltro exec-root <cmd> # as root
zeltro bash # interactive shell
zeltro shell # framework-aware REPL (tinker / django shell / node / python3; bash otherwise)
zeltro supervisor restart all # restart in-container processes
zeltro supervisor-status
Use zeltro supervisor, never zeltro exec supervisorctl — the latter runs as the developer user and is denied on the supervisor socket.
Laravel extras#
zeltro db-refresh # fresh migration + seed
zeltro cache-refresh # clear all caches
zeltro phpcs app/ # static analysis
zeltro phpcbf app/ # auto-fix
zeltro phpmd app/File.php
zeltro php -l app/File.php
Adopting an existing project#
zeltro setup my-project # a folder already in ~/zeltro-projects/
zeltro setup my-project --framework django # force detection
zeltro setup my-project --overwrite-env # repoint an existing .env at shared services
zeltro setup my-project --no-startup # register without starting, to review the compose
Framework detection reads the project's files: artisan, manage.py, main.py, app.py, package.json, wp-config.php, and a composer.json that requires drupal/core. A main.py or app.py that imports Flask is Flask; any other main.py is treated as FastAPI. For Node projects, the dependencies in package.json decide between Next.js, Nuxt, SvelteKit, Astro, Hono, Fastify, Express, NestJS, React, Vue and plain Node.
Spotted a mistake? Edit this page on GitHub.