💡 An important addition on deploying a Go application in Docker, if net/http is used
Tags: go, docker, beginners, devops
Published: 2023-10-17 05:41:48

Hey, everybody! 🙌

This is a short note article that I can't help but share with the community, as I ran into this problem myself in my latest [wonderful-readme-stats][wrs_url] project.

So, this will be a very short post, but so important for anyone who wants to deploy a Go application that uses the built-in net/http package, in a Docker container.

OK, here we go!

<a name="toc"></a>
### 📝 Table of contents

- [Description of the problem](#description-of-the-problem)
- [Useful links for explanation](#useful-links-for-explanation)
- [Solving the problem](#solving-the-problem)

<a name="#description-of-the-problem"></a>
## Description of the problem

I have a helper in my Go project that helps me make a request to the GitHub API and return a response. It is absolutely simple, but saves time as it is used in many places in the code (DRY):

```go
// customHTTPClient makes an HTTP request to download the image from the given URL and returns the response.
func customHTTPClient(uri string) (*http.Response, error) {
	// Check, if the URL is valid.
	_, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}

	// Create a new HTTP client with options.
	client := &http.Client{
		Timeout: 15 * time.Second,
		Transport: &http.Transport{
			TLSHandshakeTimeout:   10 * time.Second,
			ResponseHeaderTimeout: 10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	// Make an HTTP request to download the image from the given URL.
	req, err := http.NewRequest(http.MethodGet, uri, http.NoBody)
	if err != nil {
		return nil, err
	}
	defer req.Body.Close()

	// Set authorization header.
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("GITHUB_TOKEN"))

	// Send the request to the HTTP server.
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
```

You may have noticed that I use strictly defined options to create a custom HTTP client. This is done for a reason! For more information, see this Cloudflare [blog post][cf_blog_url].

> 📝 Note: Building the project for the Docker image is done automatically with [GoReleaser][goreleaser_url]. Therefore, I will not cover `Dockerfile` in detail.

Next, I run the finished image as always with this command on the remote server (Ubuntu 22.04):

```bash
docker run -d -p 9876:9876 koddr/wonderful-readme-stats:latest
```

But in the logs, I see the following error when trying to make a request from `http.Client` to the GitHub API:  `tls: failed to verify certificate: x509: certificate signed by unknown authority`.

Wait a minute, why is that? 🤔

[↑ Table of contents](#toc)

<a name="#useful-links-for-explanation"></a>
## Useful links for explanation

Here are some useful links that helped me understand the cause of the problem and solve it:

- https://en.wikipedia.org/wiki/Certificate_authority
- https://stackoverflow.com/a/76309552
- https://support.hashicorp.com/hc/en-us/articles/8107320508947-x509-certificate-signed-by-unknown-authority-

Feel free to post your own in the comments.

[↑ Table of contents](#toc)

<a name="#solving-the-problem"></a>
## Solving the problem

The solution was so simple that I didn't even believe it! 

It was enough to add a Docker volume pointing to the directory of the server containing the certificates (`/etc/ssl/certs`) when starting the container to make it work:

```diff
docker run -d \
  -p 9876:9876 \
+  -v /etc/ssl/certs:/etc/ssl/certs:ro \
  koddr/wonderful-readme-stats:latest
```

> ❗️ Important: Don't forget to add Docker volume in read-only mode (`:ro`).

And now the container works without errors and displays awesome statistics about the defined repository! 🎉

[↑ Table of contents](#toc)

<a name=""></a>
## Photos and videos by

- Vic Shóstak https://github.com/koddr

<!-- Article links -->

[wrs_url]: https://github.com/koddr/wonderful-readme-stats
[cf_blog_url]: https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
[goreleaser_url]: https://goreleaser.com/intro/

<a name=""></a>
## P.S.

If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! 😻

And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!

My projects that need your help (and stars) 👇

- 🔥 [gowebly][gowebly_url]: A next-generation CLI tool for easily build amazing web applications with Go on the backend, using htmx & hyperscript and the most popular atomic/utility-first CSS frameworks on the frontend.
- 🍭 [wonderful-readme-stats][wonderful_readme_stats_url]: A wonderful out-of-the-box and self-hosted solution for displaying statistics (stargazers, contributors, etc.) about your repository in your README file.
- ✨ [create-go-app][cgapp_url]: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.
- 🏃 [yatr][yatr_url]: Yet Another Task Runner allows you to organize and automate your routine operations that you normally do in Makefile (or else) for each project.
- 📚 [gosl][gosl_url]: The Go Snippet Library provides snippets collection for working with routine operations in your Go programs with a super user-friendly API and the most efficient performance.
- 🏄‍♂️ [csv2api][csv2api_url]: The parser reads the CSV file with the raw data, filters the records, identifies fields to be changed, and sends a request to update the data to the specified endpoint of your REST API.
- 🚴 [json2csv][json2csv_url]: The parser can read given folder with JSON files, filtering and qualifying input data with intent & stop words dictionaries and save results to CSV files by given chunk size.

<!-- Footer links -->

[gowebly_url]: https://github.com/gowebly
[cgapp_url]: https://github.com/create-go-app
[wonderful_readme_stats_url]: https://github.com/koddr/wonderful-readme-stats
[yatr_url]: https://github.com/koddr/yatr
[gosl_url]: https://github.com/koddr/gosl
[csv2api_url]: https://github.com/koddr/csv2api
[json2csv_url]: https://github.com/koddr/json2csv