notes-api/api
directory and create a Dockerfile.dev
file. Put the following code in it:Dockerfile
that you worked with in the previous section. The three differences in this file are as follows:npm install
instead of npm run install --only=prod
because we want the development dependencies also.NODE_ENV
environment variable to development
instead of production
.notes-db
- A database server powered by PostgreSQL.notes-api
- A REST API powered by Express.jsDockerfile
for building images, Docker Compose uses docker-compose.yaml
file to read service definitions from.notes-api
directory and create a new docker-compose.yaml
file. Put following code into the newly created file:docker-compose.yaml
file starts by defining the file version. At the time of writing, 3.8
is the latest version. You can look up the latest version here.services
block holds the definitions for each of the services or containers in the application. db
and api
are the two services that comprise this project.db
block defines a new service in the application and holds necessary information to start the container. Every service requires either a pre-built image or a Dockerfile
to run a container. For the db
service we're using the official PostgreSQL image.db
service, a pre-built image for the api
service doesn't exist. Hence, we use the Dockerfile.dev
file.volumes
block defines any name volume needed by any of the services. At the time it only enlists db-data
volume used by the db
service.docker-compose.yaml
file, lets have a closer look at the individual services.db
service is as follows:image
key holds the image repository and tag used for this container. We're using the postgres:12
image for running the database container.container_name
indicates the name of the container. By default containers are named following <project directory name>_<service name>
syntax. You can override that using container_name
.volumes
array holds the volume mappings for the service and supports named volumes, anonymous volumes, bind mounts. The syntax <source>:<destination>
is identical to what you've seen before. environment
map holds the values of the various environment variables needed for the service.api
service is as follows:api
service doesn't come with a pre-built image instead what it has is a build configuration. Under the build
block we define the context and the name of the Dockerfile for building an image. You should have an understanding of context and Dockerfile by now so I won't spend time explaining those.image
key holds the name of the image to be built. If not assigned the image will be named following the <project directory name>_<service name>
syntax.environment
map, the DB_HOST
variable demonstrates a feature of Compose. That is, you can refer to another service in the same application by using its name. So the db
here, will be replaced by the IP address of the api
service container. The DB_DATABASE
and DB_PASSWORD
variables have to match up with POSTGRES_DB
and POSTGRES_PASSWORD
respectively from the db
service definition.volumes
map, you can see an anonymous volume and a bind mount described. The syntax is identical to what you've seen in previous sections.ports
map defines any port mapping. The syntax, <host port>:<container port>
is identical to the --publish
option you used before.volumes
is as follows:<project directory name>_<volume key>
syntax and the key here is db-data
. You can learn about the different options for volume configuration in the official docs.--project-name
switch or the COMPOSE_PROJECT_NAME
environment variable). So, since this file doesn't include that information, you can look for a "notes-api_default" network after bringing up the composed project below.up
command. The up
command builds any missing images, creates containers and starts them in one go.docker-compose.yaml
file is. This is very important for every docker-compose
command you execute.--detach
or -d
option here functions same as the one you've seen before. The --file
or -f
option is only needed if the YAML file is not named docker-compose.yaml
but I've used here for demonstration purpose.up
command there is the start
command. The main difference between these two is the start
command doesn't create missing containers, only starts existing containers. It's basically same as the container start
command.--build
option for the up
command forces a rebuild of the images. There are some other options for the up
command that you can see on official docs.container ls
command, there is the ps
command for listing containers defined in the YAML only.container ls
output, but useful when you have tons of containers running simultaneously.container exec
command, there is an exec
command for docker-compose
. Generic syntax for the command is as follows:npm run db:migrate
command inside the api
service, you can execute the following command:container exec
command, you don't need to pass the -it
flag for interactive sessions. docker-compose
does that automatically.logs
command to retrieve logs from a running service. The generic syntax for the command is as follows:api
service execute the following command:-f
or --follow
option. Any later log will show up instantly in the terminal as long as you don't exit by pressing ctrl + c
key combination or closing the window. The container will keep running even if you exit out of the log window.down
command. The down
command stops all running containers and removes them from the system. It also removes any networks:--volumes
option indicates that you want to remove any named volume defined in the volumes
block. You can learn about the additional options for the down
command in the official docs.stop
command which functions identically to the container stop
command. It stops all the containers for the application and keeps the contianers. These containers can later be started with the start
or up
command.Dockerfile.dev
files in this sub-section (except the one for the nginx
service) as they are identical to some of the others you've already seen in previous sub-sections.fullstack-notes-application
directory. Each directory inside the project root contains the code for each services and the corresponding Dockerfile
.docker-compose.yaml
file let's look at a diagram of how the application is going to work:/api
in it. If yes, the router will route the request to the back-end or if not, the router will route the request to the front-end.api
service./notes-api/nginx/development.conf
and /notes-api/nginx/production.conf
files. Code for the /notes-api/nginx/Dockerfile.dev
is as follows:/etc/nginx/conf.d/default.conf
inside the container.docker-compose.yaml
file. Apart from the api
and db
services there will be the client
and nginx
services. There will also be some network definitions that I'll get into shortly.networks
block is as follows:networks
block in each of the service definitions. This way the the api
and db
service will be attached to one network and the client
service will be attached to a separate network. The nginx
service however will be attached to both the networks so that it can perform as router between the front-end and back-end services.http://localhost:8080
and voilร !Makefile
. Explore them to see how you can run this project without the help of docker-compose
like you did in the previous section.