How to Set Up Separate Test and Development Databases in Docker (using Node.js, PostgreSQL, Knex.js) Part 2
docker-compose.yml
In the root directory of your project create another file called docker-compose.yml. This is a YAML file that will define how your Docker containers should behave. Below is an example of what your docker-compose.yml could look like. You can find more information on how to set up other versions of this file in the Docker Services documentation.
version: "2"
services:
api:
build: .
command: npm start
volumes:
- .:/app
ports:
- 8000:8000
depends_on:
- postgres_dev
environment:
- NODE_ENV=$NODE_ENV
postgres:
image: postgres:9.6.2-alpine
volumes:
- ./.data:/var/lib/postgresql/data
environment:
POSTGRES_DB: db_dev
POSTGRES_USER: username
POSTGRES_PASSWORD: $POSTGRES_PW
postgres_test:
image: postgres:9.6.2-alpine
environment:
POSTGRES_DB: db_test
POSTGRES_USER: username
POSTGRES_PASSWORD: $POSTGRES_PW
Although I will go over each section of code more thoroughly, the important thing to note here is that there are 2 separate services for 2 different databases, one for the test environment and one for the development environment. Services are the actual containers for different pieces of the app. Using 2 different services for test and development databases allows us to run clean seeds and migrations each time tests are run while persisting data over runtimes for the development environment.
version
Specifying the version of the Compose file format is important for defining Docker Engine compatibility, Compose structure and permitted configuration keys, and Compose’s behavior with regard to networking. You can find more information about choosing the Compose version in the Docker docs.
services
In this section of code you will be delineating the containers you plan to run. As previously mentioned, each service corresponds to a container for a section of your app. (Individual services can actually run multiple containers which is convenient for scaling, but we won’t worry about that for now. If you’d like more information you can read more about that in the Docker docs as well.)
As shown in the above code we have defined 3 different services:
api - This is where the backend code of the api lives
postgres_dev - This container holds the database for the development environment
postgres_test - This container holds the database for the test environment
api
build
This command utilizes the Dockerfile to build a container from the specified path. In this case, we are building the container from our project’s root directory. Docker then looks for the Dockerfile and uses the image and commands specified there to build the container.
command
Technically, this instruction will override the default command specified in the CMD directive in the Dockerfile. You may see some apps that only use CMD in the Dockerfile and others that use both command in the docker-compose.yml and CMD in the Dockerfile. Either way, this instruction is telling Docker to run the command line instruction that follows.
In this case we are instructing Docker to run the npm start script.
volumes
Volumes can be used to persist data in Docker containers that would otherwise be lost when a container stops running. After the container mounts it creates the specified volumes. The contents of the host system path specified before the colon are then copied and used to populate the volume at the path inside the container specified to the right of the colon.
ports
This maps host ports to exposed container ports.
depends_on
This command alerts Docker of the order in which to start each service. The service listed here will be started before the service that depends on it. In this case the api service is explicitly saying it depends on the postgres_dev service so the postgres_dev service will be started first. This does not mean that Docker will wait until the postgres_dev service is ready to spin up the api service, it simply means postgres_dev will be initiated first.
environment
Here we are declaring relevant environment variables for this service. These environment variables will be set inside the container running for this service. In this case we are setting the NODE_ENV to the environment that is specified in the host environment during initialization.
postgres
image
This specifies the image to run the container from.
volumes
Here you will notice that one postgres container declares a volume, whereas the other one does not. This is because we want the data in the development container to persist over runtimes, but we want the test container to empty and re-populate each run.
In postgres_dev Docker creates a folder at the path ./.data and mounts that folder into the container. Docker copies the data from the local host and populates it in the file at /var/lib/postgresql/data in the container.
environment
The environment variables declared here include the name of the database this container is running as well as the username and password to access the database.
**This is part 2 of a 3-part article. You can find part 1 here and part 3 here.