Writing Dockerfiles is a pain in the ass.
All you want to do is publish your SPA or PWA dist files in a docker container and maybe even push it to a private registry.
Instead of having to write a Dockerfile, pull some base OS, increase the size of the container image, add potential attack surfaces / vulnerabilities coming from those base OS images, you can wrap and embed it in a Go binary.
Install ko
Create a ko-build.sh in the project root
1 2 3 |
#!/bin/bash ko login registry.domain.tld -u myuser -p mypassword KO_DOCKER_REPO=registry.domain.tld/user/my-frontend ko build --platform=all |
1 2 |
go mod init git.domain.tld/user/repo touch main.go |
and edit main.go and insert the following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package main import ( "embed" "io/fs" "log" "net/http" "strings" ) //go:embed dist var pwa embed.FS //go:embed dist/index.html var index embed.FS var indexFile []byte func init() { var e error indexFile, e = index.ReadFile("dist/index.html") if e != nil { log.Fatal(e.Error()) } } func main() { root, e := fs.Sub(pwa, "dist") if e != nil { log.Fatal(e.Error()) } log.Fatal(http.ListenAndServe(":8080", Handler(root))) } func Handler(root fs.FS) http.Handler { handler := http.FileServer(http.FS(root)) return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { _path := req.URL.Path // static files if strings.Contains(_path, ".") || _path == "/" { handler.ServeHTTP(w, req) return } w.Write(indexFile) }) } |
assuming your bundler, e.g. vite, places dist files in your project root’s dist
dir (vite default),
1 2 3 4 5 6 |
dist ├── assets │  ├── index-D-1oofZ3.css │  └── index-DMGf-fk6.js ├── index.html └── vite.svg |
If your dist files are in dist/pwa
and your index file is at dist/pwa/index.html
edit the lines accordingly.
This is effectively what would be in nginx:
1 2 3 |
location / { try_files $uri $uri/ /index.html; } |
Your container exposes port 8080
Build and publish
1 |
bash ko-build.sh |
Since you only need to write this once, you can copy the main.go to every project that requires it and adjust the ko-build.sh script.
Yes, this survives CTRL+F5 reloads. It routes everything through index.html unless it’s a static file that exists on the filesystem.