I am trying to make the connection with PostgreSQL using Docker Compose. I have a Golang CLI application called flow and it creates, views, adjust's, gets, and removes the budget information.
When I write the docker docker-compose up -d
, it creates two images and one container.
Images are
Container is
Command execution
When I run this command, docker exec -it <Container-ID> psql -U posgresql
, it put in the PostgreSQL terminal to handle the database.
When I run the postgres, docker run -it -e POSTGRES_PASSWORD=password postgres
, it gives me the message that database system is ready to accept connections
.
When I run the flow-budget_planner in another tab while the Postgres image is running, I get the connection refused message. Here is the command: docker run -it flow-budget_planner ./flow budget create -c category -a 300
What should I do to make the connection established and enter the data in PostgreSQL successfully using Docker? Please tell me if you need more info.
Here is the folder structure of it:
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── cmd
│ ├── budget
│ │ ├── budget.go
│ │ └── handler
│ │ ├── adjust.go
│ │ ├── create.go
│ │ ├── get.go
│ │ ├── remove.go
│ │ └── view.go
│ └── other services
│ └── service.go
├── db
│ └── budget_db
│ ├── connection.go
│ └── migrations
│ └── 001_create_budget_table.sql
├── docker-compose.yml
├── go.mod
├── go.sum
└── internal
├── app
│ └── budget
│ └── budget.go
└── middleware
└── env.go
Here is the PostgreSQL connection code
type Variables struct {
Host string
Port string
User string
Password string
DBName string
SSLMode string
}
func Connection() (*sql.DB, error) {
v := Variables{
Host: middleware.LoadEnvVariable("host"),
Port: middleware.LoadEnvVariable("port"),
User: middleware.LoadEnvVariable("user"),
Password: middleware.LoadEnvVariable("password"),
DBName: middleware.LoadEnvVariable("dbname"),
SSLMode: middleware.LoadEnvVariable("sslmode"),
}
connectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", v.Host, v.Port, v.User, v.Password, v.DBName, v.SSLMode)
db, err := sql.Open("postgres", connectionString)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}
Here is the Dockerfile
FROM golang:1.22.0-alpine3.19
RUN mkdir -p /app
WORKDIR /app
COPY go.mod /app
COPY go.sum /app
COPY . /app
RUN go mod download
RUN go build -o flow .
CMD ["./flow", "budget"]
Here is the Docker Compose file
version: "3.8"
services:
postgres:
image: postgres:latest
hostname: localhost
container_name: PostgresContainer
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: flow
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
budget_planner:
build:
context: .
dockerfile: Dockerfile
depends_on:
- postgres
environment:
DATABASE_URL: postgresql://postgres:password@localhost:5432/flow
volumes:
postgres_data:
driver: local
Your code seems to refer to these env variables:
v := Variables{
Host: middleware.LoadEnvVariable("host"),
Port: middleware.LoadEnvVariable("port"),
User: middleware.LoadEnvVariable("user"),
Password: middleware.LoadEnvVariable("password"),
DBName: middleware.LoadEnvVariable("dbname"),
SSLMode: middleware.LoadEnvVariable("sslmode"),
}
But the only env variable you provide to the application is the connection string DATABASE_URL
:
environment:
DATABASE_URL: postgresql://postgres:password@localhost:5432/flow
Shouldn't you be using that?
Like this:
sql.Open("postgres", middleware.LoadEnvVariable("DATABASE_URL"))
Or if that does not work, try manually adding the connection string first:
sql.Open("postgres", "postgres://postgres:password@postgres:5432/flow")
The connection string itself is wrong: postgresql://postgres:password@localhost:5432/flow
. localhost
when used in docker container refers to the localhost of the container itself, not to the localhost of the host machine. Containers started with docker-compose can refer to each other with the service name as the hostname by default.
The driver name might need to be be postgres
too, not postgresql
, though it might be interchangeable.
This url should work anyway:
postgres://postgres:password@postgres:5432/flow
Edit: Both postgres
and postgresql
work, according to Postgres documentation.