Category: Programming

  • Adventures in Deploying Golang and Docker

    I have some functionality on my app up and running. Wanted to start the process of figuring out the deployment process. The app consist of a Golang executable running Gin and SQLite. I want to be able to deploy the application using a Docker/OCI image so that I have maximum flexibility of where I can deploy.

    Building the initial image

    The first thing I had to do was create the initial Docker file and build the initial image. I used the Golang image based on apline. Here is what the Docker file looks like:

    # Latest golang image on apline linux
    FROM golang:1.24.1-bookworm
    
    # Work directory
    WORKDIR /build
    
    # Installing dependencies
    COPY go.mod go.sum ./
    
    # Installs Go dependencies
    
    RUN go mod download
    
    # Copying all the files
    COPY . .
    
    # Starting our application
    CMD ["go", "run", "main.go"]
    
    # Exposing server port
    EXPOSE 8080

    I built the initial image using this command:

    docker build -f Dockerfile -t mygoapp:latest .

    Built the initial image and was able to run the application. There was a problem, the image was almost 2GB in size. The Go code wasn’t substantial at this point so most of the 2GB overhead was the image itself.

    Reducing the image size

    To reduce the size, I decided to do a multi-stage build and compile the application statically. In order to do this I had to compile the Go code using the previous image (golang:1.24.1-bookworm) and copy the executable to a small base image, in this case scratch. Here is what the Dockerfile looks like

    # Use Go 1.24 bookworm as base image
    FROM golang:1.24.1-bookworm AS builder
    
    # Creates an app directory to hold your app’s source code
    WORKDIR /build
     
    # Copies everything from your root directory into /app
    COPY go.mod go.sum ./
     
    # Installs Go dependencies
    RUN go mod download
    
    # Copy the entire source code into the container
    COPY . .
     
    # Builds your app with optional configuration
    RUN CGO_ENABLED=0 GOOS=linux go build -o /app 
     
    # Tells Docker which network port your container listens on
    
    FROM scratch
    
    # Copy the executable to the scratch image
    COPY --from=builder /app /app
    
    # Expose the port for Gin
    EXPOSE 8080
    
    ENTRYPOINT ["/app"]

    I built the image using this command:

    docker build -f Dockerfile -t mygoapp:latest .

    I ran the Docker image and got this error

    [error] failed to initialize database, got error Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work.

    Golang and SQLite Present an Interesting problem

    It seems that in order to use SQLite and Golang together, you have to have CGO_ENABLED set to 1 use additional linker options to create a completely static executable. Here are the options I used

    RUN CGO_ENABLED=1 GOOS=linux go build -o /app -a -ldflags '-linkmode external -extldflags "-static"' .

    I rebuilt the image and reran it. The image started without an issue. The image size was around 70 megabytes. I ran some initial tests against the endpoints exposed and ran into this error:

    x509: certificate signed by unknown authority

    The service hits a 3rd party API and scratch doesn’t have any ssl certs installed. I could download the certificates manually but I’m lazy and don’t want to complicate the build process and dockerfile.

    Google Distroless to the Rescue

    Google actually provides minimal Docker images that are a little larger than scratch but contain things like libc, libssl, ca-certificates, etc. I’m going to use the base image (gcr.io/distroless/base). Here is what my new Dockerfile looks like:

    # Use Go 1.24 bookworm as base image
    FROM golang:1.24.1-bookworm AS builder
    
    # Creates an app directory to hold your app’s source code
    WORKDIR /build
     
    # Copies everything from your root directory into /app
    COPY go.mod go.sum ./
     
    # Installs Go dependencies
    RUN go mod download
    
    # Copy the entire source code into the container
    COPY . .
     
    # Builds your app with optional configuration
    RUN CGO_ENABLED=1 GOOS=linux go build -o /app -a -ldflags '-linkmode external -extldflags "-static"' .
     
    # Tells Docker which network port your container listens on
    
    FROM gcr.io/distroless/base
    COPY --from=builder /app /app
    COPY --from=builder /build/templates/* ./templates/
    EXPOSE 8080
    
    ENTRYPOINT ["/app"]

    Everything worked locally and the Docker image was about 100 megabytes. In order to deploy it to a cloud provider I chose to do a multi-platform image that will run on my M4 Mac (arm64) and in the cloud on non-arm hardware (amd64). Here is the command I used to build the image:

    docker buildx build --platform linux/amd64,linux/arm64 . -t mygoapp:latest

    Questions or comments feel free to reach out to me on BlueSky (https://bsky.app/profile/rooseveltanderson.bsky.social)

  • I Suck at REACT

    I’m working on a project with a Golang backend and a REACT frontend. Built the backend using Gin and was able to create a basic API. I wanted to not only test out the api but also figure out additional API calls I would need for the frontend. I’m primarily backend engineer and who can write frontend code when I had to. I didn’t feel like creating an entire REACT app and going through the process of figuring out the deployment so I decided to be lazy and serve html via Gin.

    Using HTML Templates in Go

    Writing HTML template in Go is not that difficult. Its basically a static html file with an action pipeline. A few examples of action pipelines:

    {{/* a comment */}}	
    {{.}}	
    {{.Title}}	
    {{if .Done}} {{else}} {{end}}	
    {{range .Todos}} {{.}} {{end}}	
    {{block "content" .}} {{end}}	
    Defines a comment
    Renders the root element
    Renders the “Title”-field in a nested element
    Defines an if-Statement
    Loops over all “Todos” and renders each using {{.}}
    Defines a block with the name “content”

    Here is an example template :

    <html>
        <header>
            <title>My Template</title>
        </header>
        <body>            
                {{range .Players}}
                    <div id="playerList">
                        {{.Rank}} - {{.Name}}
                    </div>
                {{end}
        </body>
    </html>
    

    Serving the Pages Via Gin

    Now that I have a template how do I serve it via Gin. First create a directory named templates and save the above example as index.tmpl .Data for the template must be passed using a Go struct. The files must be loaded using LoadHTMLGlob() or LoadHTMLFiles().

    Here is an example of how to serve html file using Gin:

    func main() {
    	router := gin.Default()
    	router.LoadHTMLGlob("templates/*")
            data := []struct {
    		Rank string
    		Name string
    	}{
    		{"1", "Michael Jordan"},
    		{"2", "Larry Bird"},
    	}
    	router.GET("/index", func(c *gin.Context) {
    		c.HTML(http.StatusOK, "index.tmpl", gin.H{
    			"Players": data,
    		})
    	})
    	router.Run(":8080")
    }
    
    

    This code will read templates from the templates directory, create an array with the required data and display the template using the provided data. Gin will detect if a change has been made to templates and automatically reload it. Templates can use CSS and Javascript if you can to get more fancy.

    This is a simple workaround to avoid having to create a full blown REACT app and shouldn’t be seen as a long term solution.

  • Working on a New Project

    I’m working on a new project. Can’t really disclose a lot but I can tell you its going to be based on Golang/REACT and I’m hoping to scale it to handle at least 100k hits per day. Because I’m a cheap bastard, I’m going to try to spend as little money as possible. I plan to document the entire process here.

    Technologies Used

    • Golang
    • Docker
    • SQLite
    • React

    3rd Party Services Used

    • Oracle Cloud Infrastructure/Google Cloud
      • Hosting the Golang backend
    • OCI NoSQL database/Firebase
      • Hosting user generated data
    • SupaBase
      • User authenitcation
    • PostHog
      • Analytics
    • Vercel
      • Hosting the React Frontend

    Why SQLITE?

    The project that I’m working on relies on a lot of static data. By storing this data in an SQLite, I’m decreasing the need to make additional network calls to a remote database. The initial data would consist of about 50,000 rows in the SQLite database which would take up about 5-6 megabytes. Each database would be deployed in a docker container with the Golang application.

    SQLite can easily handle 100k http requests per day (You can read more about uses for SQLite here). I’m enabling WAL mode (Write-Ahead Logging) to make the database more performant since the database would only be accessed via a single host.

    Feel free to comment or reach out to me on Bluesky (https://bsky.app/profile/rooseveltanderson.bsky.social)