<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yanuar Arifin</title>
    <description>The latest articles on DEV Community by Yanuar Arifin (@ynrfin).</description>
    <link>https://dev.to/ynrfin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1498662%2F44798692-7958-47a2-b3a7-4228e5e39397.png</url>
      <title>DEV Community: Yanuar Arifin</title>
      <link>https://dev.to/ynrfin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ynrfin"/>
    <language>en</language>
    <item>
      <title>Use Golang Migrate on Docker Compose</title>
      <dc:creator>Yanuar Arifin</dc:creator>
      <pubDate>Mon, 27 May 2024 09:01:13 +0000</pubDate>
      <link>https://dev.to/ynrfin/use-golang-migrate-on-docker-compose-50o5</link>
      <guid>https://dev.to/ynrfin/use-golang-migrate-on-docker-compose-50o5</guid>
      <description>&lt;p&gt;Previously I setup docker compose for golang application and PostgreSQL. It can run the application and can connect to PostgreSQL. While doing basic CRUD, I found that I need a tool to migrate my database structure and seed the database for easy onboarding and development. Thus I want to use &lt;code&gt;golang-migrate&lt;/code&gt; to do that. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Golang Migrate
&lt;/h2&gt;

&lt;p&gt;Contrary to the name, it is not a migration tool specifically  created for golang development, although we can use it as a golang package in your application. This tool is a CLI tool that can be installed on Windows, Mac, and Linux. As a CLI tool, it means that no matter what language you use to code, your migration can be managed using this &lt;code&gt;golang-migrate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/golang-migrate/migrate/blob/master/cmd/migrate/README.md"&gt;Here&lt;/a&gt; is the documentation how to install it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Golang migrate commands
&lt;/h2&gt;

&lt;p&gt;What I use is this for now. &lt;/p&gt;

&lt;p&gt;Create migration script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;migrate create &lt;span class="nt"&gt;-ext&lt;/span&gt; sql &lt;span class="nt"&gt;-dir&lt;/span&gt; migrations &lt;span class="nt"&gt;-seq&lt;/span&gt; create_users_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;migrate&lt;/code&gt; golang-migrate command&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;create&lt;/code&gt; create new migration script, both up and down&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-ext sql&lt;/code&gt; use &lt;code&gt;sql&lt;/code&gt; extension&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-dir migrations&lt;/code&gt; the target directory where the migration files is generated&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-seq&lt;/code&gt; make the file name sequential, increment from 1. if not it would be current datetime&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;create_users_table&lt;/code&gt; migration files name&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which will create 2 files in &lt;code&gt;migrations&lt;/code&gt; dir&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    ├── Dockerfile
    ├── Dockerfile.multistage
    ├── README.md
    ├── cmd
    │   └── main.go
    ├── controllers
    ├── docker-compose.yml
    ├── go-market-warehouse-api
    ├── go.mod
    ├── go.sum
    ├── main
    ├── middlewares
    ├── migrations
&lt;span class="gi"&gt;+++ │   ├── 000001_create_users_table.down.sql &amp;lt;- this
+++ │   └── 000001_create_users_table.up.sql   &amp;lt;- and this
&lt;/span&gt;    ├── models
    └── repositories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;000001_create_users_table.up.sql&lt;/code&gt; contains the DDL for the changes that I want to make, in this case creating &lt;code&gt;users&lt;/code&gt; table&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;"uuid-ossp"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;uuid_generate_v4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;000001_create_users_table.down.sql&lt;/code&gt; contains the DDL to undo the above code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="nv"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Apply the migration once the DDL is supplied in the files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;migrate -path /migrations/ -database "postgres://username:password@host:port/db_name?sslmode=disable" up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the database tables before we execute the migration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7k928e3zb6nxfzdlqtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7k928e3zb6nxfzdlqtc.png" alt="Table list before migration" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here it is after running migration&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx8jc299hvuah42343al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx8jc299hvuah42343al.png" alt="Table list after migration" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;schema_migrations&lt;/code&gt; is used by golang migrate to track migrations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;users&lt;/code&gt; table is the table that being generated from my script above&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How To Use Golang Migrate in Docker Compose
&lt;/h2&gt;

&lt;p&gt;Golang migrate has its own image in docker hub. The way to use this, is to create a new service for this golang migrate, then run the migration that targetted to PostgreSQL service. By creating it's own service and execute it, the migration is another service that is not added to the main application service, make the application smaller.&lt;/p&gt;

&lt;p&gt;Here's the new &lt;code&gt;golang-migrate&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... go-market-warehouse-api &amp;amp; local-pg-16 services&lt;/span&gt;
  &lt;span class="na"&gt;migrate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;migrate/migrate&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt; 
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./migrations/:/migrations&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-path"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/migrations/"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-database"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://${PGUSER}:${PGPASSWORD}@local-pg-16:5432/postgres?sslmode=disable"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full code &lt;a href="https://github.com/ynrfin/go-market-warehouse-api/tree/83ed175ea03b75cad75f36f63c7295ef8f33352f"&gt;in this commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;image: migrate/migrate&lt;/code&gt; base this service to the image of &lt;code&gt;golang-migrate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;depends_on: local-pg-16: condition: service_healthy&lt;/code&gt;  I rearrange the application startup order to &lt;code&gt;PostgreSQL&lt;/code&gt;  then &lt;code&gt;golang-migrate&lt;/code&gt; then &lt;code&gt;go-market-warehouse-api&lt;/code&gt; because after we startup the database, I want the database to be updated using latest DDL then the application can connect to database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;networks&lt;/code&gt; use already declared network, which is &lt;code&gt;my-local-net&lt;/code&gt; to connect to PostgreSQL service&lt;/p&gt;

&lt;p&gt;&lt;code&gt;volumes&lt;/code&gt; here I specify which directory contains the migration scripts, in my case, it is the &lt;code&gt;migrations&lt;/code&gt; directory, the LEFT one &lt;code&gt;./migrations/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;commands&lt;/code&gt; is the arguments the &lt;code&gt;migrate&lt;/code&gt; service use to run the migration&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;commands&lt;/code&gt; is the bash command to apply the migration(s) like the one above without using docker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;migrate &lt;span class="nt"&gt;-path&lt;/span&gt; /migrations/ &lt;span class="nt"&gt;-database&lt;/span&gt; &lt;span class="s2"&gt;"postgres://&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PGUSER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PGPASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@local-pg-16:5432/postgres?sslmode=disable"&lt;/span&gt; up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the docker log with migration executed&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsjbwmwsgqakv7clq7x6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsjbwmwsgqakv7clq7x6.png" alt="Docker log for migrate execution" width="800" height="804"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;migrate-1&lt;/code&gt; is the service name&lt;br&gt;
&lt;code&gt;1/u create_users_table&lt;/code&gt; 1 migration upped, with name of create_users_table&lt;/p&gt;

&lt;p&gt;If no new migration found, there won't be message on the log.&lt;/p&gt;

&lt;p&gt;That is it for this article, hope you enjoy it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>docker</category>
      <category>database</category>
      <category>go</category>
    </item>
    <item>
      <title>Setting Up Docker Compose for Golang Application</title>
      <dc:creator>Yanuar Arifin</dc:creator>
      <pubDate>Fri, 24 May 2024 06:56:14 +0000</pubDate>
      <link>https://dev.to/ynrfin/setting-up-docker-compose-for-golang-application-2cf2</link>
      <guid>https://dev.to/ynrfin/setting-up-docker-compose-for-golang-application-2cf2</guid>
      <description>&lt;p&gt;This post will explain how I create Dockerfile and docker-compose.yml for golang development. The full code can be seen &lt;a href="https://github.com/ynrfin/go-market-warehouse-api/tree/b88913f0a2f01fd1a73d43bff7ea76cb127cdaf7"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code for Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:1.22 &lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod go.sum ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-o&lt;/span&gt; /go-market-warehouse-api ./cmd/main.go

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "/go-market-warehouse-api" ]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FROM golang:1.22&lt;/code&gt; is basing the  image to current newest golang version available&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;  tell docker to create and make this &lt;code&gt;/app&lt;/code&gt; directory as the current directory the docker is in&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY go.mod go.sum ./&lt;/code&gt; will copy &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;go.sum&lt;/code&gt; from project directory to &lt;code&gt;/app&lt;/code&gt; directory inside docker &lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN go mod download&lt;/code&gt; will download the dependency listed in go.mod that we just copied&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY . ./&lt;/code&gt; will copy content in current directory (the project root dir ). Basically copying the project to docker&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN CGO_ENABLED=0 GOOS=linux go build -o /go-market-warehouse-api ./cmd/main.go&lt;/code&gt; is building go binary to from &lt;code&gt;cmd/main.go&lt;/code&gt; and give output to root dir &lt;code&gt;/&lt;/code&gt; and use  &lt;code&gt;go-market-warehouse-api&lt;/code&gt; as the binary name, same as &lt;code&gt;go-market-warehouse-api.exe&lt;/code&gt; in windows&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXPOSE 8000&lt;/code&gt; tell the image it builds to open port 8000 to outside the image&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD [ "/go-market-warehouse-api" ]&lt;/code&gt; will run the binary that we create&lt;/p&gt;

&lt;p&gt;This setup &lt;code&gt;Dockerfile&lt;/code&gt; will create an image that is around 1 Gb. Big but it contains necessary tools to build golang app. Suitable for development.&lt;/p&gt;

&lt;h2&gt;
  
  
  building docker-compose.yml
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create dedicated volume and network
&lt;/h3&gt;

&lt;p&gt;The purpose for this &lt;code&gt;docker-compose.yml&lt;/code&gt; is to run the golang application and a database in my application stack. My database choice is PostgreSQL.&lt;/p&gt;

&lt;p&gt;Before we create the file, first I will create new docker &lt;code&gt;network&lt;/code&gt; for the application and database to communicate. And a &lt;code&gt;volume&lt;/code&gt; to persist data for the database, so when we restart the database, the data that we previously insert still exist.&lt;/p&gt;

&lt;p&gt;Create  volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker volume create pg-16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a volume called &lt;code&gt;pg-16&lt;/code&gt;. You can check it using &lt;code&gt;docker volume list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F299wkw0jmz0zj52q33fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F299wkw0jmz0zj52q33fx.png" alt="Docker volume before and after docker volume create" width="720" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create -d bridge my-local-net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new bridge network called &lt;code&gt;my-local-net&lt;/code&gt;. Check with &lt;code&gt;docker network list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4aqt7a970hu01dbnzkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4aqt7a970hu01dbnzkg.png" alt="Docker network before and after docker network create" width="720" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;bridge&lt;/code&gt; part is a network that can be accessed inside the docker&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Service
&lt;/h3&gt;

&lt;p&gt;Create the application service &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;go-market-warehouse-api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# This will be uncommented when db service is introduced&lt;/span&gt;
    &lt;span class="c1"&gt;# depends_on:&lt;/span&gt;
    &lt;span class="c1"&gt;#  local-pg-16:&lt;/span&gt;
    &lt;span class="c1"&gt;#    condition: service_healthy&lt;/span&gt;

    &lt;span class="c1"&gt;# always restart when app crashes&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api:v1.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:8080&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGUSER=${PGUSER:-totoro}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPASSWORD=${PGPASSWORD:?database password not set}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGHOST=${PGHOST:-db}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPORT=${PGPORT:-5432}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGDATABASE=${PGDATABASE:-mydb}&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;services&lt;/code&gt; this mark the list of available services when we run docker compose. In this case, the services that is available only 1, called &lt;code&gt;go-market-warehouse-api&lt;/code&gt;(the one under &lt;code&gt;services:&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;build&lt;/code&gt; and &lt;code&gt;context: .&lt;/code&gt;: will build from current directory where docker-compose.yml is located(indicated by &lt;code&gt;.&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;image&lt;/code&gt; which image it will use to build. when not available locally, it will search from docker registry&lt;/p&gt;

&lt;p&gt;&lt;code&gt;container_name&lt;/code&gt; the name of  the container to be built&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hostname&lt;/code&gt; how can this service be called by other service in the network. Docker container can refer to other services location(ip) using this &lt;code&gt;hostname&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;networks&lt;/code&gt;: the network this service attached to&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ports&lt;/code&gt;: exposed ports by this service&lt;/p&gt;

&lt;p&gt;&lt;code&gt;environment&lt;/code&gt; : setting environment variable&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deploy&lt;/code&gt; and its child specs :  it will deploy the service and restart it when failure happens&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Service
&lt;/h3&gt;

&lt;p&gt;This is the description of the database service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tes-pg:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${PGPASSWORD}&lt;/span&gt;
    &lt;span class="c1"&gt;# Make sure postgres is ready to accept connection as the indicator that &lt;/span&gt;
    &lt;span class="c1"&gt;# Postgres is ready&lt;/span&gt;
    &lt;span class="c1"&gt;# ref https://www.postgresql.org/docs/current/app-pg-isready.html&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${PGUSER}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;# a more complete command&lt;/span&gt;
      &lt;span class="c1"&gt;# test: ["CMD-SHELL", "pg_isready -U ${PGUSER} -d ${PGDATABASE} -h 127.0.0.1"]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tes-pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# this will always create new network&lt;/span&gt;
  &lt;span class="c1"&gt;# my-local-net:&lt;/span&gt;
  &lt;span class="c1"&gt;#   driver: bridge&lt;/span&gt;
  &lt;span class="na"&gt;my-local-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mynet&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;image: postgres:16.2&lt;/code&gt; : I want to use the current latest postgres, which is 16.2. Make sure you specify the major version(16 in this case), as the default is the latest, and when the version change, it almost always has incompatibility &lt;/p&gt;

&lt;p&gt;&lt;code&gt;container_name&lt;/code&gt;: the name of the container to be built&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hostname&lt;/code&gt;: address for this service in the docker network&lt;/p&gt;

&lt;p&gt;&lt;code&gt;networks&lt;/code&gt;: specify which network this service attached to, so when you want to access this service, you need to attach to this network and use &lt;code&gt;hostname&lt;/code&gt; to connect to the database. If the network is not specified, docker compose will create new network and attach all services described in the yml file to it, so all services can communicate with each other. I use predefined network because it will be easier when other container outside the one described in this &lt;code&gt;docker-compose.yml&lt;/code&gt; to connect to the services here&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ports&lt;/code&gt; : mapping exposed port from container to host(the computer)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;volume&lt;/code&gt;: persist the data created by postgre to disk. So when we restart the container, it can resume using data from volume instead of creating data. Other container can use this data too. Beware though when you use different version of postgres, it could result in corrupt data(I did it with different mysql version before lol)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;environment&lt;/code&gt;: this will setup the postgres password when we want to enter the postgres. You could setup username ,password, and database name, the default is &lt;code&gt;postgres&lt;/code&gt; or if the username is specified and database name is not, it would default to username. I read the documentation &lt;a href="https://hub.docker.com/_/postgres"&gt;here on POSTGRES_DB section&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;healthcheck&lt;/code&gt; : is how I defined healthcheck that I will use in the application service(the &lt;code&gt;condition&lt;/code&gt; on &lt;code&gt;depends_on&lt;/code&gt; key in &lt;code&gt;go-market-warehouse-api&lt;/code&gt; service). The &lt;code&gt;depends_on&lt;/code&gt; is to tell the &lt;code&gt;go-market-warehouse-api&lt;/code&gt; that it should start when the the database service is deemed to be healthy, which I define as the database ready to accept TCP connection. Why it is necessary? if I don't specify the &lt;code&gt;depends_on&lt;/code&gt; the application will try to connect to db when db is not fully instantiated or ready  or both, so the application would throw error. If I only specify &lt;code&gt;depends_on: local-pg-16&lt;/code&gt;, docker would start creating the application service after the database service is not yet ready to accept TCP connection. The are use cases when you need to seed data to db before the db is ready to accept connection too. Hence I use the healthcheck function&lt;/p&gt;

&lt;p&gt;&lt;code&gt;volumes:&lt;/code&gt; I specify &lt;code&gt;volume&lt;/code&gt; here to point it at the predefined volume that I execute before I create the &lt;code&gt;docker-compose.yml&lt;/code&gt;. If &lt;code&gt;external: true&lt;/code&gt; is not specified, docker-compose will create new volume that has the name of &lt;code&gt;&amp;lt;project-name&amp;gt;_tes-pg&lt;/code&gt;, same as network&lt;/p&gt;

&lt;p&gt;&lt;code&gt;network&lt;/code&gt;: specify which network to be used. &lt;code&gt;my-local-net&lt;/code&gt; here is the name, &lt;code&gt;name: mynet&lt;/code&gt; is the network name on the docker, and &lt;code&gt;external: true&lt;/code&gt; is same as the volume, it tells docker to use predefined network  that exists on the docker(external of this &lt;code&gt;docker-compose.yml&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Here's the full &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;go-market-warehouse-api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="c1"&gt;# always restart when app crashes&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api:v1.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:8080&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGUSER=${PGUSER:-totoro}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPASSWORD=${PGPASSWORD:?database password not set}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGHOST=${PGHOST:-db}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPORT=${PGPORT:-5432}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGDATABASE=${PGDATABASE:-mydb}&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
  &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tes-pg:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${PGPASSWORD}&lt;/span&gt;
    &lt;span class="c1"&gt;# Make sure postgres is ready to accept connection as the indicator that &lt;/span&gt;
    &lt;span class="c1"&gt;# Postgres is ready&lt;/span&gt;
    &lt;span class="c1"&gt;# ref https://www.postgresql.org/docs/current/app-pg-isready.html&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${PGUSER}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;# a more complete command&lt;/span&gt;
      &lt;span class="c1"&gt;# test: ["CMD-SHELL", "pg_isready -U ${PGUSER} -d ${PGDATABASE} -h 127.0.0.1"]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tes-pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# this will always create new network&lt;/span&gt;
  &lt;span class="c1"&gt;# my-local-net:&lt;/span&gt;
  &lt;span class="c1"&gt;#   driver: bridge&lt;/span&gt;
  &lt;span class="na"&gt;my-local-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mynet&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  On &lt;code&gt;healthcheck&lt;/code&gt; and &lt;code&gt;depends_on&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;depends_on&lt;/code&gt; is to tell the &lt;code&gt;go-market-warehouse-api&lt;/code&gt; that it should start when the the database service is deemed to be healthy, which I define as the database ready to accept TCP connection. Why it is necessary? If I only specify &lt;code&gt;depends_on: local-pg-16&lt;/code&gt;, docker would start creating the application service after the database service is not yet ready to accept TCP connection. Try by modify &lt;code&gt;depends_on: local-pg-16&lt;/code&gt; then run the docker compose, if you see the log, sometimes the app throws error could not connect to database and database still printing out logs when application log started even though you have specify &lt;code&gt;depends_on&lt;/code&gt;. Hence I use &lt;code&gt;depends_on: condition: service_healthy&lt;/code&gt; to specify when the database is deemed to be ready is when the &lt;code&gt;condition: service_healthy&lt;/code&gt; is fulfilled. &lt;/p&gt;

&lt;p&gt;what is the criteria for &lt;code&gt;service_healthy&lt;/code&gt;? it use the definition of &lt;code&gt;healthcheck&lt;/code&gt; in &lt;code&gt;local-pg-16&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here it is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;go-market-warehouse-api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="c1"&gt;# always restart when app crashes&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api:v1.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go-market-warehouse-api&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:8080&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGUSER=${PGUSER:-totoro}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPASSWORD=${PGPASSWORD:?database password not set}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGHOST=${PGHOST:-db}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGPORT=${PGPORT:-5432}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGDATABASE=${PGDATABASE:-mydb}&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;restart_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
  &lt;span class="na"&gt;local-pg-16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-pg-16&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;my-local-net&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tes-pg:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${PGPASSWORD}&lt;/span&gt;
    &lt;span class="c1"&gt;# Make sure postgres is ready to accept connection as the indicator that &lt;/span&gt;
    &lt;span class="c1"&gt;# Postgres is ready&lt;/span&gt;
    &lt;span class="c1"&gt;# ref https://www.postgresql.org/docs/current/app-pg-isready.html&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${PGUSER}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="c1"&gt;# a more complete command&lt;/span&gt;
      &lt;span class="c1"&gt;# test: ["CMD-SHELL", "pg_isready -U ${PGUSER} -d ${PGDATABASE} -h 127.0.0.1"]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tes-pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# this will create new network based on project directory name&lt;/span&gt;
  &lt;span class="c1"&gt;# my-local-net:&lt;/span&gt;
  &lt;span class="c1"&gt;#   driver: bridge&lt;/span&gt;
  &lt;span class="na"&gt;my-local-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# kind of alias for this docker-compose.yml&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mynet&lt;/span&gt; &lt;span class="c1"&gt;# mynet is the name when you run `docker network ls`&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try the project by clone this &lt;a href="https://github.com/ynrfin/go-market-warehouse-api/tree/b88913f0a2f01fd1a73d43bff7ea76cb127cdaf7"&gt;repo’s commit&lt;/a&gt;. Read the description on how to run the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note on network and volume
&lt;/h2&gt;

&lt;p&gt;In many tutorial, network and volume is declared WITHOUT &lt;code&gt;external&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tes-pg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my-local-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new network with default name &lt;code&gt;&amp;lt;project-name&amp;gt;_tes-pg&lt;/code&gt; and &lt;code&gt;&amp;lt;project-name&amp;gt;_my-local-net&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk1t37vf1rz5lrddusoa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk1t37vf1rz5lrddusoa.png" alt="Volume and Network without  raw `external:true` endraw " width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;external: true&lt;/code&gt;, no additional network and volume like the one above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsvkdzsymg81r1ia4ixl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsvkdzsymg81r1ia4ixl.png" alt="Volume and network when  raw `external:true` endraw " width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These screenshots use &lt;a href="https://github.com/jesseduffield/lazygit"&gt;lazygit&lt;/a&gt; btw.&lt;/p&gt;

&lt;p&gt;That’s all for this article, hope you learn something new.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>go</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
