Documentation

From a repository to a running service.

Drumlin builds your application from source and runs it. This page is enough to get an application deployed, to give it state and a domain, and to know where to look when you need to understand what it is doing. Everything described here is reachable from the dashboard, from the command line, and from the same API. They are one product with three front doors.

#Deploy an application

A service is one running application. It belongs to an environment inside a project, and it is built either from a Git repository or from a container image you already have.

From a repository

Connect a Git provider to your organisation, then create a service pointing at a repository and a branch. Drumlin resolves that branch to an exact commit for every build, so a deployment always names the code it ran.

Your repository needs a Dockerfile. Drumlin builds it and pushes the result to the platform registry.

From an image you did not build

A service does not need a repository at all. Give it the name of an image and Drumlin runs it: vaultwarden/server, nginx, redis, or anything else that is published somewhere Drumlin can reach. Most self-hosted software is distributed exactly this way, and running it here is the same three fields as running your own code.

# all of these are accepted
vaultwarden/server
ghcr.io/example/tool:v2.3.1
registry.example.com/team/api@sha256:…

A tag is resolved to the exact image it pointed at, once, when the service is created or when you ask for it again. What runs afterwards is those bytes and not the tag: somebody republishing :latest tomorrow does not change what your service is running, and the deployment record names the digest; the label it arrived under may have moved since.

Updating therefore happens when you ask for it. Ask Drumlin to resolve the tag again and it picks up whatever it points at now, as a new release you can roll back from like any other.

The registry has to be one Drumlin can read without credentials. Docker Hub, GitHub's registry and the other public ones all are; a registry that needs a password of yours is not supported yet, and the service says so as you create it instead of waiting and then failing on an image it cannot pull.

#Releases and rollback

Every successful build produces a release: an immutable image, pinned by digest, with the commit and the build that made it. A service runs exactly one release at a time.

Because a release is immutable, going back deploys the old image again and builds nothing:

$ drumlin releases api
$ drumlin rollback api 23

Scaling is separate from deploying. Changing the number of copies does not build anything:

$ drumlin scale api --copies 3

#Databases

Create a managed PostgreSQL database in a project and grant a service access to it. Access is per service: a database is not readable by everything in the project simply because it exists there.

$ drumlin databases
$ drumlin databases url orders-db

A database runs as a single instance, sized when you create it. It is archived continuously to object storage in a different data centre from the one it runs in, and recovery to a point in time reads those archives, never anything held on the same machine as the database.

#Persistent volumes

A volume is disk attached to a service at a path you choose, and it survives deployments: a new release starts with the same files the last one left behind. Most applications do not need one, and anything that keeps its state in a database should not, but a media store, a search index or a self-hosted tool that owns its own data does.

Volumes are created against a service and mounted where you say. A service with a volume runs as a single copy, because two copies writing to one disk is not a configuration the platform will let you ask for by accident.

Copies, and putting one back

Every volume is copied to object storage in a different data centre. A newly created volume gets its first copy within seconds and does not wait for the next scheduled run. After that the copies are dated and kept.

You can restore a volume yourself, from the dashboard, without asking anybody: choose the volume, choose a date, and confirm. The service is stopped, the files are replaced with that copy, and the service is started again. It is deliberately not a silent operation. The restore appears in the service's own activity log, and while it is running the service reports itself as restoring, so nothing shows it as healthy.

A restore replaces what is there now. Putting a volume back to yesterday discards anything written since. The dashboard says which copy it is about to use and how large it is before it will let you continue, and a copy that holds nothing is refused outright, so it cannot be used to empty a volume that still has data in it.

#Variables and secrets

Configuration belongs to a service. Set variables on it and they are in the environment of every instance the next time it starts; mark one as a secret and it is encrypted with a key the database on its own cannot decrypt.

A secret can be read back, and you will need to eventually. Reading one is written into the organisation's activity log with who did it and when. That is the trade the platform makes: a secret you can read back has to be treated as readable, and the log is what makes that visible.

Every set of variables is kept as a version, so a change that broke something is a change you can see. Granting a service access to a database writes its connection string in as a variable, in the same operation as the grant, so there is no step where the credential exists and the service cannot reach it.

#What an image may do

A container runs inside a sandbox with its own kernel boundary. Inside that boundary it can do what an ordinary image needs at start-up: listen on a low port, change ownership of its own files, and drop to its own user. The official nginx, postgres and redis images all do exactly that, and all start here unmodified.

What a container cannot do is reach the machine underneath it: mount a filesystem, create a device node, open a raw socket, trace another process, load a kernel module. An image that needs any of those was normally built to own a machine.

If something is refused, the service's diagnostics name it, so you do not have to dig it out of a start-up log. The message says the platform refused it, so you are not left to conclude your image is broken.

#Domains and TLS

Every service has an address of its own from the moment it starts, something like api-7bvb32zq.apps.drumlin.cloud, and answers on it over HTTPS with no setup at all.

To use your own hostname, add it to the service and point the records Drumlin shows you at the region. Once those records resolve, the certificate is obtained and then renewed for as long as the domain stays attached. Renewal happens on our side, so no certificate here has an expiry date you need to diarise.

$ drumlin domains api
$ drumlin domains add api www.example.com

Until the records are right, the domain is listed as unverified and says what it is waiting for. A certificate that cannot be issued is reported as exactly that, and the service is not marked broken because of it.

Turning the Drumlin address off

Once your own domain is answering you will usually want the generated one to stop. Two front doors to the same application is not normally what anybody meant: a search engine can index the second one, a cookie or a redirect written for one hostname misbehaves on the other, and a staging service somebody assumed was private is reachable by anyone who guesses eight characters.

So it is a switch, on the service's Domains page. Turn it off and the name stops answering within a minute; turn it back on and the same address returns, not a new one, so anything you had pasted it into still works.

With no custom domain, turning it off takes the service off the internet entirely. That is allowed on purpose: an API that only the rest of its environment talks to has no business having a public address. Nothing stops running and nothing is deleted, and the other services in the same environment go on reaching it by name. The dashboard asks before doing it.

#Services talking to each other

A repository often holds more than one deployable thing, a frontend and the API it calls. Create one Drumlin service per thing, all in the same environment, and point each at its own directory and Dockerfile.

Each service is then reachable from the others by its own name, on port 80, whatever port the application itself listens on. A frontend proxying to a service called notifier uses:

proxy_pass http://notifier;

Port 80, not the application's own port. Drumlin puts every service behind the same port inside the environment, so an upstream written as notifier:3001 will not connect even when the name resolves.

Only services in the same environment can reach each other. Production cannot talk to staging, and no other customer can reach either. That boundary is enforced in the network, not by configuration.

#Logs and metrics

Logs and metrics belong to the service. There is no agent to install and no second account to create.

$ drumlin logs api
$ drumlin logs api --build 01M1V4…

#Console

When reading logs is not enough, open a shell inside a running instance:

$ drumlin shell api

A console session is authenticated, single-use and expires. It is recorded against the service in the activity log, next to the deployments. A shell is the most invasive thing the platform can do, so it is visible where somebody would think to look for it.

#People and roles

An organisation is the thing that owns projects, services and the bill. Invite people to it by email address and give each of them a role: owner, co-owner, admin, developer or viewer. A role can be narrowed further per project, so somebody who works on one application does not get the rest of the estate with it.

Exactly one person is the owner. Ownership moves from one account to another in a single step, so there is no way to end up with two owners or with none.

The activity log

Everything anybody did is in the organisation's own log: deployments, scaling, variables revealed, people invited and removed, databases created and restored. It is append-only, and the database itself enforces that, so no code path can rewrite an entry.

That includes us. If our staff look at your organisation or act on it, the entry appears in your log, naming what they did, without you having to ask for it and without us being able to remove it.

#Billing

Drumlin looks at what your account is holding at the top of every hour, prices it, and writes the reading down. At the start of each month those readings become one invoice for the month that ended.

Compute is counted only while a service is running, so stopping something stops its cost at the next hour. Disk is counted whether anything is running or not, because a disk holds its bytes either way.

What it costs

Processor€0.012per core-hour
Memory€0.006per GiB-hour
Disk€0.00015per GiB-hour, for databases and volumes

There is no platform fee, no per-seat charge and no credit to buy in advance. A quarter of a core and half a gibibyte, which is the default size of a service, is €4.38 a month. The same service with a ten-gibibyte database beside it is €9.86.

The month while it is happening

The billing screen shows what the current month has cost so far, exactly, and an estimate of where it will end up if nothing changes. The two are labelled differently on purpose: one is a measurement and the other is a projection from the most recent hour.

Every hourly reading is there behind it. Any figure on an invoice can be traced to the hours that produced it, which is the point of keeping them.

Paying

An invoice is issued at the start of the month for the month before, and is payable within fourteen days by bank transfer or by card. VAT is added according to where you are: German VAT for a German customer, none for a business elsewhere in the EU with a VAT number we have confirmed against the European Commission's register, and none outside the union.

An invoice that goes unpaid gets one reminder. Ten days after that the services in the account stop. Your data is not touched, and everything starts again within minutes of the invoice being settled. There is no collection process beyond that.

An account needs invoice details before it can run anything chargeable. An invoice has to carry a name, an address and a country, and the country is what decides the tax. We ask for them before the first service, because asking after a month of usage means asking somebody to volunteer an address for work already done.

#API and webhooks

Everything the dashboard does, the API does. It is the same API, and the command line is one of its clients. Issue a token for your continuous integration and it can deploy, scale, read logs and everything else the role you gave it allows.

A token belongs to an organisation and carries a role, so a token for a deploy pipeline does not have to be able to remove people. Tokens are listed with when they were last used, and revoking one takes effect immediately.

Webhooks

Register a webhook and Drumlin posts to your own endpoint when something happens: a deployment succeeded or failed, a build failed, a service became unhealthy, a domain was verified, a certificate could not be issued. Each delivery is signed, and every attempt is recorded with the response it got, so a webhook that is not arriving leaves a record you can read.

Email notifications cover the same events, and which of them reach you is yours to choose per organisation.

Your own account

Two-factor authentication is available with an authenticator app, and comes with recovery codes for the day the phone is gone. You can see every session your account has open, end any of them, and end all of them at once. You can also close the account yourself and have its data deleted, without writing to anybody.

#Command line

The CLI talks to the same API as the dashboard and returns the same messages.

$ drumlin login
$ drumlin use acme/storefront
$ drumlin services

$ drumlin deploy api --wait
Build 24 of api, from acme/storefront at 7b21d9a.
  Fetching the repository
  Building the image
  Pushing the image
Built in 18s.

Other commands you will reach for: services show, start, stop, scale, domains, databases url, shell, use and whoami. Every one of them takes --json if you would rather parse it than read it.

#Regions

Services run in NL01, Netherlands 1, which is the only region today. We own and operate it. Further regions will be listed when they exist.

Something missing from this page? It covers the paths most people need first. The dashboard describes each screen as you use it, and the CLI's --help is generated from the same definitions as the API.