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.
#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.
#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.
- Log output from every instance, live or historical
- Processor and memory use per instance
- Instance health, and the state of the release that is running
- An activity log recording who changed what, and when
$ 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.012 | per core-hour |
|---|---|---|
| Memory | €0.006 | per GiB-hour |
| Disk | €0.00015 | per 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.
#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.
--help is generated from the same definitions as the API.