Solving Protohackers with Fly.ioSat 24 September 2022
Tagged: software, protohackers
Fly.io can host anything that can run in a Docker container, and you don't even need to write your own Dockerfile because it automatically handles the most common languages and frameworks. I wish the tooling were less verbose, it's as if it's got debug mode logging permanently enabled. It is also quite slow. In principle we don't need any more than building a tiny binary and scping it to a server, why does it take multiple minutes to make a deployment?
Using Fly.io is slightly more trouble than using a normal VPS, so if you already have a VPS you will probably find that easier. Depending on your ISP, your router, and how comfortable you are with port forwarding, Fly.io could be either more or less trouble than hosting your program at home. If you think hosting at home sounds easy, then that is probably easier for you than Fly.io.
Let's assume that you don't want to pay for a VPS, and you can't host at home (maybe you are behind CGNAT). In that case Fly.io is your next best option!
Firstly, install Fly.io using the instructions on their website, which is currently:
$ curl -L https://fly.io/install.sh | sh
Follow its instructions to put its environment variables in your bashrc. Signup for Fly.io:
$ fly auth signup
This will try to open a page in your browser to let you either connect a GitHub account or sign up with email.
Make a directory for your project and copy the Go example program from the bottom of the Protohackers help page into main.go.
Fly.io is supposed to be able to auto-detect a Go program and deploy it with just fly launch, but I have found it doesn't auto-detect Go unless you have a go.mod. (You may have better luck with other languages). To help it detect your Go program, run:
$ go mod init example.com/foo go: creating new go.mod: module example.com/foo
Now we'll launch it on Fly.io. I accepted the defaults for everything.
$ fly launch Creating app in /home/jes/protofly Scanning source code Detected a Go app Using the following build configuration: Builder: paketobuildpacks/builder:base Buildpacks: gcr.io/paketo-buildpacks/go ? App Name (leave blank to use an auto-generated name): Automatically selected personal organization: James Stanley ? Select region: lhr (London, United Kingdom) Created app old-surf-8338 in organization personal Wrote config file fly.toml ? Would you like to set up a Postgresql database now? No ? Would you like to deploy now? No Your app is ready. Deploy with `flyctl deploy`
The default Fly.io configuration is to run an HTTP proxy in front of your application, but that's not what we want.
We need to edit the generated fly.toml file to make the [[services]] section expose TCP directly rather than proxying with HTTP. There is more information in their UDP and TCP documentation.
Clear out the [[services]] section of fly.toml and replace it with:
[[services]] internal_port = 10000 protocol = "tcp" [[services.ports]] port = 10000
And deploy the application with fly deploy. This part is extremely verbose. It would benefit from the Rule of Silence. It also takes quite a long time. You just have to wait.
$ fly deploy [... hundreds of lines redacted ...] 1 desired, 1 placed, 1 healthy, 0 unhealthy --> v2 deployed successfully
Once our application is "deployed successfully", it's up and listening on the public Internet. Despite the hundreds of lines of output that we were shown, I think we actually haven't been told what IP address our program is listening on. In my case the application was called old-surf-8338, and we can find out what IP address it's listening on by resolving old-surf-8338.fly.dev:
$ host old-surf-8338.fly.dev old-surf-8338.fly.dev has address 184.108.40.206 old-surf-8338.fly.dev has IPv6 address 2a09:8280:1::a:77b4
So in my case we'll put 220.127.116.11 and port 10000 into the checker form:
And the check should pass!
Great success indeed.
If you like my blog, please consider subscribing to the RSS feed or the mailing list: