Give a project a home
$ grim server:add-project shop --host=vps1 --domain=shop.example.com
The server is set up and the project has never been there. A release has nowhere to land yet: no database, no domain pointing at anything, no directory and no .env. grim server:add-project builds that place, once per project and environment. It starts no containers. The first grim release does.
You come back to it later for three reasons: to register a staging environment next to production, to move a project onto a certificate of your own, and to repair a server-side piece that went missing.
Usage
$ grim server:add-project shop --host=vps1 --domain=shop.example.com
$ grim server:add-project shop --host=vps2 --domain=staging.shop.example.com --env=staging
$ grim server:add-project shop --host=vps1 --domain=shop.example.com --cert=~/certs/shop.pem --key=~/certs/shop-key.pemRun it from the project directory. It reads grim.json there to learn whether the project runs Horizon and Reverb, which image it pulls and which themes are active. --host and --domain are both required. There is no default domain; without one the command stops with --domain is required. Point the domain's DNS at the server first, or the certificate cannot be issued.
Arguments and options
Prepare a server for a new GRIM project (DB, nginx, Docker config)
Usage
grim server:add-project [--host HOST] [--domain DOMAIN] [--env ENV] [--horizon] [--reverb] [--no-ssl] [--cert CERT] [--key KEY] [--reset-env] [--no-db] [--force-vhost] [--] <project>Arguments
| Argument | Description |
|---|---|
project |
Project name (required) |
Options
| Option | Description |
|---|---|
--host=HOST |
Server host or alias |
--domain=DOMAIN |
Domain for the project |
--env=ENV |
Environment (production, staging, …) — defaults to "production". The same project can be registered to multiple environments on different hosts. (default: production) |
--horizon |
Enable Horizon queue worker |
--reverb |
Enable Reverb WebSocket server |
--no-ssl |
Skip SSL certificate setup |
--cert=CERT |
Bring-your-own TLS certificate (PEM: leaf + intermediates) — host nginx serves it, certbot is skipped |
--key=KEY |
Private key (PEM) belonging to --cert |
--reset-env |
Overwrite an existing remote .env (rotates APP_KEY and the DB password) instead of keeping it |
--no-db |
Skip database creation |
--force-vhost |
Bypass the "domain already served by another project" pre-flight check (advanced — may leave orphaned SSL / vhost behind) |
What it actually does
Before anything is changed it looks through the server's nginx vhosts for the domain. Then the steps run, each printed with its result. For project shop in production:
- Uploads your certificate, only with
--certand--key, to/etc/ssl/grim-shop/. The key is readable by root alone. - Creates the MySQL database and user, both named
shop, with a random password. The user may connect fromlocalhostand from the Docker address range. - Writes the nginx vhost
/etc/nginx/sites-available/grim-shop.conf, enables it and reloads nginx. It passes the domain to Traefik on port 8080. - Gets a certificate from Let's Encrypt with certbot, which also adds the redirect from HTTP. A failure here is reported in the summary and does not stop the run.
- Creates
/opt/shop, withdocker/and thestorage/tree inside. - Uploads
docker-compose.ymlanddocker/nginx-production.confinto it. - Generates
.env, with a newAPP_KEY, the database credentials and, with Reverb, its keys. - Records the project in your
~/.grim/servers.json: host, domain, path, database name and TLS mode.
The summary prints names and never secrets. The database password and the app key exist in the server's .env and nowhere else; read them with grim env:pull.
Three ways to serve TLS
| Mode | Pass | What nginx serves |
|---|---|---|
| Let's Encrypt | nothing | A certificate certbot issues and renews. Needs the email grim server:setup recorded and DNS that already points at the server. |
| Your own certificate | --cert and --key |
The pair you uploaded, with a redirect from HTTP. It is checked on your machine first and must cover the domain. Renewal is running the command again with the new pair. |
| None | --no-ssl |
Plain HTTP, and APP_URL starts with http://. |
The mode is remembered. A later run without --cert on a project that has its own certificate keeps that certificate and says so.
Running it again
On a project that is already live the command is careful. It finds /opt/shop/.env, keeps it and skips the database step, so the APP_KEY, the password in force and everything grim env:set wrote survive. Only the scheme of APP_URL and ASSET_URL is brought in line with the TLS mode. Domains attached with grim server:add-domain stay on the vhost.
--reset-env writes a fresh .env over the live one: a new APP_KEY and a new database password. Everything the app encrypted with the old key becomes unreadable, and values set by hand are gone. Pull a copy first.
More than one environment
--env registers another environment of the same project, usually on another server. Production keeps the bare name. Every other environment gets its own, so two of them can share a host without touching each other.
production |
--env=staging |
|
|---|---|---|
| Directory | /opt/shop |
/opt/shop-staging |
| Database and user | shop |
shop_staging |
| Vhost | grim-shop.conf |
grim-shop-staging.conf |
The first environment you register becomes the project's default, the one grim release targets without an argument. grim release staging reaches the other.
Pitfalls
The domain already belongs to another project
Domain "shop.example.com" is already served by another project's nginx vhost on 203.0.113.10: stops the run before anything is written. Two vhosts with one name make nginx use the first and ignore the second without a word. The message prints the way out: retire the old project with grim server:remove-project, keeping its certificate and database with --keep-ssl --keep-db.
The run is green and the site has no certificate
Look at the SSL row of the summary. failed, with certbot's reason in brackets, nearly always means the DNS record does not point at the server yet. The exact certbot command to repeat is printed under the table. skipped (no acme_email recorded for this server) means setup never stored an email; run grim server:setup again with --email.
The name you typed is not the name in grim.json
⚠ Project arg "shop" differs from grim.json deploy alias "webshop". is asked as a question for a reason. grim release and the env: commands look the project up by the name in grim.json, and would not find what you are about to register. Use the same name, or set deploy.project in grim.json as the message suggests.
Running it again takes the password off
The vhost is written without Basic Auth and the project is recorded as unprotected. After a second run on a protected site, switch it back on with grim server:auth.
After it finishes
The summary ends with the command that comes next. Release into the new home, then add what the project still needs.
$ grim release
$ grim server:add-domain shop www.shop.example.comThe second domain is grim server:add-domain. The whole path, server included, is the recipe First deploy to a fresh VPS.
Questions
Can several projects share one server?
Yes. Each gets its own database and user, its own directory under /opt, its own vhost and certificate. They share the host's nginx, MySQL server and the one Traefik.
Does every colleague have to run it?
No. It registers the project in the servers.json of the machine it ran on. A colleague can release without it once grim.json names the host under deploy and their key is accepted for the deploy user.
What does --force-vhost leave behind?
Both vhost files, the old project's and the new one. Which of them nginx serves is then a matter of file order, so use it only while you move a certificate by hand.
Related spells
grim server:add-domain
Add a second domain
Serve a deployed project on one more domain, with the vhost, the Traefik rule and the certificate widened to cover it.
grim server:auth
Put a password on a site
Switch HTTP Basic Auth on or off for a deployed project, in the host nginx that stands in front of it.
grim server:remove-project
Tear a project down
Remove one environment of a project from its server, with its containers, volumes, database, vhost, certificate and directory.
grim server:setup
Raise a production server
Turn a bare Ubuntu or Debian VPS into a host for deployed projects, with Docker, MySQL, nginx, Traefik and a deploy user.
grim release
Ship in one pass
Test, build, push and deploy the project to an environment with a single command and a single tag.
Recipes that use it
First deploy to a fresh VPS
Take a bare Ubuntu or Debian server and a project that runs on your machine, and end with the project live on its own domain over HTTPS.
Staging to production, one image
Register a staging environment next to production, name both in grim.json, release to staging, look at it, then send the same tag to production without building again.