Skip to content

The daily commands ​

Every command works from anywhere inside a project - dqx walks up to find .dq/config.yml, the way git finds a repository. Standing in a site's directory also picks that site.

Starting and stopping ​

dqx upStart every site of the project, and say which one failed rather than reporting success
dqx downStop this project
dqx killStop every container on every backend, the router included, and free 80 and 443; the runtimes keep running. --mine leaves containers of no dqx project alone
dqx kill --allThat, and the runtimes too, giving their memory back
dqx updatePull the images this project declares

dqx up also brings up the shared router if it is not running, reissues the certificate when it does not cover a domain yet, and frees a port another project is holding - naming what it stopped. See the shared router for what it is and why the ports move.

Working inside a project ​

A site can be named by its domain or by its directory

A site's checkout is not its domain: one project here serves register.example… out of www/sites.example…. Both work, and so does a prefix:

bash
dqx shell register.example.local.dolphiq.eu
dqx shell sites.example.local.dolphiq.eu
dqx shell sites

Standing inside www/<checkout> needs no argument at all - dqx uses the directory you are in, and now does so in a project with no .dq/config.yml too.

dqx shellA shell in the site, in the directory you are standing in
dqx shell --site <domain>A shell in a named site, from anywhere in the project
dqx exec -- <cmd>One command in the site
dqx composer / npm / artisan / phpThe tool, in the right container and directory
dqx testThe project's test suite
dqx run <name>A command the project declared in commands:
dqx openThe site, in your browser

The dev server, and its port ​

A site whose assets come from Vite needs the dev server running inside the container, on the port this project's stack publishes:

bash
dqx dev                       # the site's dev server, on this project's own port
dqx dev --site app.acme.dqx  # when the project has more than one

The port matters and it is why this is a command rather than npm run dev. Every project owns a block of four ports, either from a pinned VITE_PORT or derived from the project itself, and its stack publishes them - so several projects, and up to four sites within one project, can run a dev server at the same time. Measured on this machine: two projects both wanted 5173, one of them with that number hardcoded in its own compose file, so only one dev server could ever start.

dqx dev passes --host --port <the site's port> --strictPort, because Vite otherwise binds its own 5173 - and then nothing listens on the published port, so the page loads while every asset fails with net::ERR_CONNECTION_REFUSED - or, with the port taken, quietly moves to one nothing publishes. It also sets VITE_PORT to the same number inside the container, for a config that reads it.

dqx dev stops the dev server in the container when it is itself stopped: Ctrl-C, a closed terminal, or a task runner's stop. docker exec passes no signal into the container without a terminal, so before this a stopped dqx dev left Vite listening.

A dev server still running on that port anyway - one started by hand, or a dqx dev that was killed outright - is named before anything starts, with the way to stop it:

text
✗ port 5220 is already taken by a dev server running for 27:57 (pid 1054): node …/vite --host --port 5220 --strictPort
  stop it with Ctrl-C where it runs, or replace it: dqx dev --replace

dqx dev --replace stops that server and starts a new one.

A project whose stack predates the shared router keeps those ports too: dqx serves such a project through an override that takes its fixed host bindings away, and that override now keeps the dev server's, because a browser is told that number by the application.

Two things a project's own vite.config.js decides, which dqx cannot:

  • https. dqx mounts the project's certificates at /etc/ssl/certs/local; a config that reads them serves the dev server over https, and the asset URLs then match the page.
  • the URL the browser is told. Laravel's Vite plugin writes the address Vite bound into public/hot, which inside a container is not an address a browser can use. A config that sets server.hmr.host to the site's domain fixes that, and keeps the port Vite actually runs on.

A port in the config wins over --port

--port moves the server, not the URL. A server.origin like https://app.acme.dqx:5173, or a server.hmr.port / clientPort, still sends the browser to that number, so the hot file says 5173 while Vite answers on the project's own port. dqx dev and dqx doctor both say so, dqx sync says it right after pinning the port, and dqx adopt says it in its preview against the port the project will get. None of them edits the config: it is the application's code.

text
vite           ! app.acme.dqx: vite.config.js sends the browser to port 5173 (server.origin), but Vite runs on 5220, so every asset is refused
                 remove server.origin, or build it from process.env.VITE_PORT, which `dqx dev` sets

Removing server.origin is usually enough: with server.hmr.host set, Laravel's plugin builds the URL from that host and the port Vite bound.

Until then, dqx dev starts Vite through an overlay, so the project works without anyone editing it first. The overlay is a small config in node_modules/.dqx/ that loads the project's own and moves only the pinned ports to the one Vite runs on; host, https and plugins stay as the project wrote them. It is never committed, and it is removed again once the config no longer needs it:

text
! vite.config.js sends the browser to port 5173 (server.origin); running Vite with a dqx overlay that moves it to 5220
  node_modules/.dqx/vite.config.mjs - remove server.origin from vite.config.js to make this unnecessary, or run with --no-overlay

The overlay reaches Vite through --config, so it is used only where the dev script is Vite itself. A script that chains or wraps it (concurrently …) gets the warning instead. --no-overlay starts Vite with the project's own config.

A project generated from the fleet's own recipes carries both; Laravel's default config carries neither. Dev server ports shows a config that does both, for Vite, Laravel Mix, webpack-dev-server and Gulp.

The database ​

dqx dbOpen the client
dqx db --urlThe URL, for TablePlus or a dump
dqx db:import <file> / db:exportIn and out
dqx snapshot / dqx restoreThe database and the storage directories

The published port is this project's own rather than 3306, so two projects never fight over one - and it stays put, so a saved connection keeps working. dqx db --url prints it, and ports: asks for a particular one.

Seeing what is going on ​

dqx dashboard puts all of this in a browser: every project, what is running, and start and stop buttons. See the dashboard.

dqx infoEvery URL, port and connection this project has
dqx info --jsonThe same, for a script or an agent
dqx listEvery project on the machine, on every backend (below)
dqx logs [service]Tail logs
dqx doctorWhat is broken here, and what to do about it
dqx memoryWhat each project is costing, and what the VM holds beyond that

What dqx list looks like ​

One row per checkout, with every domain that checkout answers to:

dqx list
txt
KEY                           PROJECT        CHECKOUT                URL                                             BACKEND  STATUS
acme                          Acme Intranet  acme-intranet.dqx       https://acme-intranet.dqx                       docker   stopped
hb                            Harbor Books   admin.harbor-books.dqx  https://admin.harbor-books.dqx                  docker   running
                                             api.harbor-books.dqx    https://api.harbor-books.dqx                    docker   running
                                             harbor-books.dqx        https://harbor-books.dqx                        docker   running
                                                                     https://www.harbor-books.dqx
hb-pr-1284                    Harbor Books   harbor-books.dqx        https://harbor-books.pr-1284.dqx                docker   running
nwd                           Northwind      northwind               https://northwind.dqx                           docker   stopped
nwd-acme-6479-invoice-export  Northwind      northwind.dqx           https://northwind.acme-6479-invoice-export.dqx  docker   stopped
old-site                      old-site       old-site.dqx            https://old-site.dqx                            docker   stopped
tp                            Tidepool       tidepool                https://tidepool.dqx                            docker   stopped

Generated

This output is rendered by the same function the command uses, against the fictional fleet dqx ships for its documentation - so it cannot drift from what the command prints. bun run docs:output rewrites it.

Xdebug ​

bash
dqx xdebug on     # and off, and `dqx xdebug` to see which

Per developer and gitignored, so it never lands in somebody else's checkout, and dqx up re-applies your choice.

dqx v1.78.0