In order to deploy you app on Convox you will need two things
A Dockerfile
A convox.yml
manifest
If you have not already containerized your application it is a relatively straightforward process. You can start by cloning a Dockerfile from one of our example apps. If we don’t have a Dockerfile for your chosen language or framework you can check the Docker Hub to find a public image to use as a starting point. We also have a guide for optimizing your Dockerfile to improve things like caching and build times.
Once you have your Dockerfile ready to go you need to create a convox.yml file. This file is a manifest that describes the components that make up your application. If you are familiar with using a docker-compose file you should find the convox.yml format to be very familiar. This guide will walk you through the basic components of the Convox manifest but you can find a complete specification here.
The Convox manifest has four major components of which only services
is required. They are:
Environment
Resources
Services
Timers
The environment section is where you can define any global environment variables that will be made available to any service in your application. Environment variables are generally used for environment specific configuration and secrets management and for this reason we generally recommend setting default values that would be appropriate for things like local development and storing your actual production values with convox env set
or the convox environment control panel in the web console for each specific deployment of your app.
Resources are network-accessible external services. Most often, resources are databases. You can see the list of currently supported resources here. When you specify a resource, Convox will pull a public docker image for the resource type and run that resource in a container inside your cluster. Any service which is linked to a resource will automatically have a connection string URL for that resource injected as an environment variable at runtime. Convox resources are durable and can be used for production but if you would prefer to use a cloud specific database service such as RDS or Cloud SQL you can do so using resource overlays which allow you to specify an external service to use in place of the containerized resource for specific environments. This can be a great cost savings technique if for example you want to run a containerized Postgres database in your dev and staging environments but use RDS in production without needing to make any code changes or special configuration.
Services are the heart of your app. Services are scalable processes defined by a Dockerfile. Services can expose a port or set of ports and can be manual or automatically scaled. The most typical example of a service would be a web application such as a Ruby, Django, NodeJS or Go app that is accessed via an https connection. By default services with ports are publicly accessible but Convox also supports internal services that can only be accessed by other services running in the same Racks which can be useful for things like internal APIs. Convox also supports special use case services such as agents and singletons. You can read the full specification for services here.
A timer is effectively a cron job. With a timer you can specify a regular schedule to spawn a process and run a specific command. Timers must reference a service defined in your convox.yml which defines the process to be spawned. If you want to define a service that will be used exclusively as a timer job you can define that service with a scale of zero. You can read the full specification for timers here
Let’s take a look at an example convox.yml. For this example we will specify a Django app with a web service, a celery worker service, and a timer that sends email reminders every five minutes using the worker service. For this example we will use a single Dockerfile for both services. This app also uses a Postgres database as a primary datastore and a Redis database as a celery broker.
environment:
- DEVELOPMENT=True
resources:
database:
type: postgres
broker:
type: redis
services:
web:
build: .
domain: ${DOMAIN}
port: 8001
resources:
- database
- broker
worker:
build: .
command: celery -A web worker -l info
resources:
- database
- broker
timers:
reminderemails:
schedule: "*/5 * * * *"
command: python manage.py sendreminders
service: worker
A few things to note here are that the web service specifies an internal port of 8001 which will be exposed externally on 443. The web service will also be made available on the domain(s) specified in the the DOMAIN environment variable and Convox will automatically provision a SSL certificate for the specified domain. The worker service on the other hand does not expose any external ports. The worker service also uses a unique startup command while the web service uses the command specified within the Dockerfile. Hopefully this gives you a sense of what a typical convox.yml might look like for a production application.
If you are already using docker-compose.yml
for your application moving to a convox.yml
is a relatively straightforward process. As an example take a look at the following docker-compose.yml
version: '2'
services:
web:
image: httpd
ports:
- 80:80
volumes:
- /tmp/something
environment:
- MY_ENVIRONMENT: development
links:
- supportservice
- redis
web_secondary:
image: myusername/privateimage:latest
command: override_command.sh
ports:
- 3001
links:
- redis
redis:
image: "redis:alpine"
supportservice:
build:
context: .
dockerfile: Dockerfile.support
links:
- postgres
postgres:
image: postgres:12
So here we have 5 different services defined, one a httpd service, one a private image with an overridden command, one a plain Redis service, and one we’re building locally from a custom Dockerfile, with a Postgres database image thrown in for good measure.
The convox.yml
manifest for this app would look like
resources:
redis:
type: redis
postgres:
type: postgres
options:
version: 12
services:
web:
image: httpd
port: 80
volumes:
- /tmp/something
enviroment:
- MY_ENVIRONMENT=development
resources:
- redis
web_secondary:
image: myusername/privateimage:latest
command: override_command.sh
port: 3001
resources:
- redis
supportservice:
build:
path: .
manifest: Dockerfile.support
resources:
- postgres
Here you can see how we have turned the Redis and Postgres services into resources, which can be accessed by the other services. As we mentioned above, those resources will be run in containers backed by durable storage (by default) but if you would prefer to use a cloud provider service you can do so using resource overlays. We have also removed the links directive as services in a Rack are able to communicate each other using our built-in service discovery. Otherwise, you will find the manifests to be nearly identical.