Skip to content

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.pem

Run 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:

  1. Uploads your certificate, only with --cert and --key, to /etc/ssl/grim-shop/. The key is readable by root alone.
  2. Creates the MySQL database and user, both named shop, with a random password. The user may connect from localhost and from the Docker address range.
  3. 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.
  4. 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.
  5. Creates /opt/shop, with docker/ and the storage/ tree inside.
  6. Uploads docker-compose.yml and docker/nginx-production.conf into it.
  7. Generates .env, with a new APP_KEY, the database credentials and, with Reverb, its keys.
  8. 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.com

The 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.

Recipes that use it