---
title: "Run a Go App"
layout: language-and-framework-docs
redirect_from: /docs/getting-started/golang/
---
<figure class="flex justify-center">
<img src="/static/images/golang.png" alt="Illustration by Annie Ruygt of a seated gopher dreaming" class="w-full max-w-lg mx-auto">
</figure>
<%= partial "partials/intro", locals: { runtime: "Go" } %>
<div class="note icon">
Already have a Go Fly App? Try adding [Tigris](https://www.tigrisdata.com/docs/overview/+external) for file storage. Tigris is scalable, secure, global object storage and you can [integrate it quickly with your Go app](#add-tigris-global-storage-optional).
</div>
## _The example application_
You can get the code for the example from [the GitHub repository](https://github.com/fly-apps/go-example). Just `git clone https://github.com/fly-apps/go-example` to get a local copy.
The `go-example` application is, as you'd expect for an example, small. It's a Go application that uses the HTTP server and templates from the standard library. Here's all the code from `main.go`:
```golang
package main
import (
"embed"
"html/template"
"log"
"net/http"
"os"
)
//go:embed templates/*
var resources embed.FS
var t = template.Must(template.ParseFS(resources, "templates/*"))
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := map[string]string{
"Region": os.Getenv("FLY_REGION"),
}
t.ExecuteTemplate(w, "index.html.tmpl", data)
})
log.Println("listening on", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
```
The `main` function starts a server that responds with an HTML page showing the fly region that served the request. The template lives in `./templates/` and is embedded into the binary using the [embed](https://golang.org/pkg/embed/) package in go 1.16+.
The template itself, `index.html.tmpl`, is very simple too:
```html
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<h1>Hello from Fly</h1>
{{ if .Region }}
<h2>I'm running in the {{.Region}} region</h2>
{{end}}
</body>
</html>
```
## _Building the application_
As with most Go applications, a simple `go build` will create a `hellofly` binary which we can run. It'll default to using port 8080 and you can view it on localhost:8080 with your browser. So, the raw application works. Now to package it up for Fly.
## _Install flyctl and log in_
We are ready to start working with Fly and that means we need `flyctl`, our CLI app for managing apps on Fly. If you've already installed it, carry on. If not, hop over to [our installation guide](/docs/flyctl/install/). Once that's installed you'll want to [log in to Fly](/docs/getting-started/sign-up-sign-in/).
## _Launch the app on Fly.io_
To launch an app on fly, run `flyctl launch` in the directory with your source code. This will create and configure a fly app for you by inspecting your source code, then prompt you to deploy.
```cmd
flyctl launch
```
```output
Scanning source code
WARN no go.sum file found, please adjust your Dockerfile to remove references to go.sum
Detected a Go app
Creating app in /path/to/your/app
We're about to launch your Go app on Fly.io. Here's what you're getting:
Organization: Your (fly launch defaults to the personal org)
Name: hellofly (derived from your directory name)
Region: Ashburn, Virginia (US) (this is the fastest region for you)
App Machines: shared-cpu-1x, 1GB RAM (most apps need about 1GB of RAM)
Postgres: <none> (not requested)
Redis: <none> (not requested)
? Do you want to tweak these settings before proceeding? n
...
Your app is ready! Deploy with `flyctl deploy`
```
First, this command scans your source code to determine how to build a deployment image as well as identify any other configuration your app needs, such as secrets and exposed ports.
After your source code is scanned and the results are printed, you'll be prompted to ask if you want to change any of the defaults `flyctl` has set.
Choosing yes will bring you to a page in your browser where you can change any configuration, such as the region to deploy into or the size of the VM created.
Once that is complete, `flyctl` will create a `fly.toml` file containing the configuration selected. It might then begin a deployment, or let you know you should start a deployment yourself via `flyctl deploy`.
## _Inside `fly.toml`_
The `fly.toml` file now contains a default configuration for deploying your app. In the process of creating that file, `flyctl` has also created a Fly-side application slot of the same name, "hellofly". If we look at the `fly.toml` file we can see the name in there:
```toml
app = 'hellofly'
primary_region = 'iad'
[build]
[build.args]
GO_VERSION = '1.21.5'
[env]
PORT = '8080'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
```
The `flyctl` command will always refer to this file in the current directory if it exists, specifically for the `app` name value at the start. That name will be used to identify the application to the Fly service. The rest of the file contains settings to be applied to the application when it deploys.
You can see in the `[build]` section the builder image and the version of Go that was detected in your `go.mod` file. You can change or add build arguments to configure the build process using the `[build.args]` section.
## _Inside `Dockerfile`_
[We use a Debian base image](https://github.com/superfly/flyctl/blob/master/scanner/templates/go/Dockerfile) which has `CGO` enabled by default. A minimal alternative without CGO is Alpine:
```diff
# ...
- FROM golang:${GO_VERSION}-bookworm as builder
+ FROM golang:${GO_VERSION}-alpine as builder
# ...
- FROM debian:bookworm
+ FROM alpine:latest
```
If you want to use Alpine with `CGO` enabled, [read here](https://megamorf.gitlab.io/2019/09/08/alpine-go-builds-with-cgo-enabled/).
## _Deploying to Fly.io_
To deploy your app, just run:
```cmd
flyctl deploy
```
This will lookup our `fly.toml` file, and get the app name `hellofly` from there. Then `flyctl` will start the process of deploying our application to the Fly platform. Flyctl will return you to the command line when it's done.
## _Viewing the deployed app_
Now the application has been deployed, let's find out more about its deployment. The command `flyctl status` will give you all the essential details.
```cmd
flyctl status
```
```output
App
Name = hellofly
Owner = demo
Hostname = hellofly.fly.dev
Image = hellofly:deployment-abcdefghxyz
Machines
PROCESS ID VERSION REGION STATE ROLE CHECKS LAST UPDATED
app 0ac9ed79 1 iad running 2024-02-13T12:52:38Z
app 1bd10fe8 1 iad stopped 2024-02-13T12:52:38Z
$
```
As you can see, the application has been deployed with a DNS hostname of hellofly.fly.dev, and an instance is running in Virginia. Your deployment's name will, of course, be different.
## _Connecting to the app_
The quickest way to browse your newly deployed application is with the `flyctl apps open` command.
```cmd
flyctl apps open
```
```output
opening https://golang-example.fly.dev ...
```
Your browser will be sent to the displayed URL.
## _Add Tigris Global Storage (optional)_
Tigris provides CDN-like accessibility for your stored objects. Tigris has S3-API compatibility, automatically manages data replication and caching for you, and offers AuthN and AuthZ authentication mechanisms. Learn more about [Tigris Object Storage](https://www.tigrisdata.com/docs/overview/+external).
1. <b>Create a Tigris bucket</b>: run the create command inside your Go Fly App's directory. Afterwards, make sure to take note of the credentials received:
```cmd
fly storage create
```
2. <b>Retrieve AWS SDK modules</b>: Tigris has an S3 compatible API service, thus, we can easily use [AWS SDK Go](https://aws.github.io/aws-sdk-go-v2/docs/getting-started/#install-the-aws-sdk-for-go-v2) modules to connect with the Tigris Bucket we've just created.
```cmd
go get github.com/aws/aws-sdk-go-v2
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/s3
```
3. <b>Create a [Client function](https://github.com/fly-apps/go-example-tigris/blob/main/tigris/tigris.go#L12)</b> that will return an instance ready to connect with a Tigris bucket:
```go
func Client(ctx context.Context) (*s3.Client, error) {
// 1. Create an aws.Config instance
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load Tigris config: %w", err)
}
// 2. Create an Amazon S3 client using the AWS Config instance created, "cfg"
return s3.NewFromConfig(cfg, func(o *s3.Options){
o.BaseEndpoint = aws.String("https://fly.storage.tigris.dev")
o.Region = "auto"
}), nil
}
```
The `LoadDefaultConfig()` function will create an [aws.Config instance](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#Config) that can load [credentials from environment variables](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials).
Passing this instance to the `NewFromConfig()` [function](https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#newfromconfig) creates an S3 service client we've configured to connect with the Tigris endpoint.
4. <b>Set up Tigris Credentials</b>.
To successfully connect with our Tigris Bucket, we can set our Tigris credentials as AWS SDK environment variables.
<b>In a local setup</b>, this should look something like:
```cmd
export AWS_ACCESS_KEY_ID=TIGRIS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY=TIGRIS_SECRET_ACCESS_KEY
```
<b>For our Go Fly App</b>, these environment variables should have already been set as [secrets](https://fly.io/docs/flyctl/secrets/) during the running of `fly storage create`:
```output
Setting the following secrets on ...:
AWS_ACCESS_KEY_ID: <AWS_ACCESS_KEY_ID_VALUE>
AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
AWS_REGION: auto
AWS_SECRET_ACCESS_KEY: <AWS_SECRET_ACCESS_KEY>
BUCKET_NAME: <BUCKET_NAME>
```
Of course, this would only automatically happen if the create command was run in a directory containing a `fly.toml` for our Go Fly app.
But if that wasn't the case, we can still manually set necessary [secrets](https://fly.io/docs/flyctl/secrets/) like so:
```cmd
fly secrets set AWS_ACCESS_KEY_ID="<TIGRIS_ACCESS_KEY_ID>"
fly secrets set AWS_SECRET_ACCESS_KEY="<TIGRIS_SECRET_ACCESS_KEY>"
```
5. <b>[Upload a file](https://github.com/fly-apps/go-example-tigris/blob/main/tigris/tigris.go#L26):</b> Aside from connecting with a Bucket, we can use other [AWS SDK functions](https://docs.aws.amazon.com/code-library/latest/ug/go_2_s3_code_examples.html). Let's use the `PutObject` function to upload a text file on the go like so:
```go
func Client( ctx context.Context ) (*s3.Client, error){}
func UploadText( ctx context.Context ) (string, error){
// 1. Create connection via client
conn, err := Client( ctx )
// 2. Upload sample file
if err==nil{
_, err = conn.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String("<TIGRIS_BUCKET_NAME>"),
Key: aws.String("sample.txt"),
Body: strings.NewReader("testing content"),
})
}
if err!=nil{
return err.Error(), err
}else{
return "success",nil
}
}
```
For further reference, please see official Tigris [documentation for AWS SDK GO](https://www.tigrisdata.com/docs/sdks/s3/aws-go-sdk/), as well as [this compact package](https://github.com/Xe/x/blob/master/tigris/tigris.go) containing helpers for interacting with Tigris.
## _Bonus points_
If you want to know what IP addresses the app is using, try `flyctl ips list`:
```cmd
flyctl ips list
```
```out
TYPE ADDRESS CREATED AT
v4 50.31.246.73 23m42s ago
v6 2a09:8280:1:3949:7ac8:fe55:d8ad:6b6f 23m42s ago
```
## _Arrived at destination_
You have successfully built, deployed, and connected to your first Go application on Fly.
Run a Go App
Getting an application running on Fly.io is essentially working out how to package it as a deployable image.
Once packaged, it can be deployed to the Fly.io platform.
In this guide we’ll learn how to deploy a Go
application
on Fly.io.
You can get the code for the example from the GitHub repository. Just git clone https://github.com/fly-apps/go-example to get a local copy.
The go-example application is, as you’d expect for an example, small. It’s a Go application that uses the HTTP server and templates from the standard library. Here’s all the code from main.go:
The main function starts a server that responds with an HTML page showing the fly region that served the request. The template lives in ./templates/ and is embedded into the binary using the embed package in go 1.16+.
The template itself, index.html.tmpl, is very simple too:
<!DOCTYPE html><htmllang="en"><head></head><body><h1>Hello from Fly</h1>
{{ if .Region }}
<h2>I'm running in the {{.Region}} region</h2>
{{end}}
</body></html>
Building the application
As with most Go applications, a simple go build will create a hellofly binary which we can run. It’ll default to using port 8080 and you can view it on localhost:8080 with your browser. So, the raw application works. Now to package it up for Fly.
Install flyctl and log in
We are ready to start working with Fly and that means we need flyctl, our CLI app for managing apps on Fly. If you’ve already installed it, carry on. If not, hop over to our installation guide. Once that’s installed you’ll want to log in to Fly.
Launch the app on Fly.io
To launch an app on fly, run flyctl launch in the directory with your source code. This will create and configure a fly app for you by inspecting your source code, then prompt you to deploy.
flyctl launch
Scanning source code
WARN no go.sum file found, please adjust your Dockerfile to remove references to go.sum
Detected a Go app
Creating app in /path/to/your/app
We're about to launch your Go app on Fly.io. Here's what you're getting:
Organization: Your (fly launch defaults to the personal org)
Name: hellofly (derived from your directory name)
Region: Ashburn, Virginia (US) (this is the fastest region for you)
App Machines: shared-cpu-1x, 1GB RAM (most apps need about 1GB of RAM)
Postgres: <none> (not requested)
Redis: <none> (not requested)
? Do you want to tweak these settings before proceeding? n
...
Your app is ready! Deploy with `flyctl deploy`
First, this command scans your source code to determine how to build a deployment image as well as identify any other configuration your app needs, such as secrets and exposed ports.
After your source code is scanned and the results are printed, you’ll be prompted to ask if you want to change any of the defaults flyctl has set.
Choosing yes will bring you to a page in your browser where you can change any configuration, such as the region to deploy into or the size of the VM created.
Once that is complete, flyctl will create a fly.toml file containing the configuration selected. It might then begin a deployment, or let you know you should start a deployment yourself via flyctl deploy.
Inside fly.toml
The fly.toml file now contains a default configuration for deploying your app. In the process of creating that file, flyctl has also created a Fly-side application slot of the same name, “hellofly”. If we look at the fly.toml file we can see the name in there:
The flyctl command will always refer to this file in the current directory if it exists, specifically for the app name value at the start. That name will be used to identify the application to the Fly service. The rest of the file contains settings to be applied to the application when it deploys.
You can see in the [build] section the builder image and the version of Go that was detected in your go.mod file. You can change or add build arguments to configure the build process using the [build.args] section.
Inside Dockerfile
We use a Debian base image which has CGO enabled by default. A minimal alternative without CGO is Alpine:
# ...
- FROM golang:${GO_VERSION}-bookworm as builder
+ FROM golang:${GO_VERSION}-alpine as builder
# ...
- FROM debian:bookworm
+ FROM alpine:latest
If you want to use Alpine with CGO enabled, read here.
Deploying to Fly.io
To deploy your app, just run:
flyctl deploy
This will lookup our fly.toml file, and get the app name hellofly from there. Then flyctl will start the process of deploying our application to the Fly platform. Flyctl will return you to the command line when it’s done.
Viewing the deployed app
Now the application has been deployed, let’s find out more about its deployment. The command flyctl status will give you all the essential details.
flyctl status
App
Name = hellofly
Owner = demo
Hostname = hellofly.fly.dev
Image = hellofly:deployment-abcdefghxyz
Machines
PROCESS ID VERSION REGION STATE ROLE CHECKS LAST UPDATED
app 0ac9ed79 1 iad running 2024-02-13T12:52:38Z
app 1bd10fe8 1 iad stopped 2024-02-13T12:52:38Z
$
As you can see, the application has been deployed with a DNS hostname of hellofly.fly.dev, and an instance is running in Virginia. Your deployment’s name will, of course, be different.
Connecting to the app
The quickest way to browse your newly deployed application is with the flyctl apps open command.
flyctl apps open
opening https://golang-example.fly.dev ...
Your browser will be sent to the displayed URL.
Add Tigris Global Storage (optional)
Tigris provides CDN-like accessibility for your stored objects. Tigris has S3-API compatibility, automatically manages data replication and caching for you, and offers AuthN and AuthZ authentication mechanisms. Learn more about Tigris Object Storage.
Create a Tigris bucket: run the create command inside your Go Fly App’s directory. Afterwards, make sure to take note of the credentials received:
fly storage create
Retrieve AWS SDK modules: Tigris has an S3 compatible API service, thus, we can easily use AWS SDK Go modules to connect with the Tigris Bucket we’ve just created.
go get github.com/aws/aws-sdk-go-v2
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/s3
Create a Client function that will return an instance ready to connect with a Tigris bucket:
funcClient(ctxcontext.Context)(*s3.Client,error){// 1. Create an aws.Config instancecfg,err:=config.LoadDefaultConfig(ctx)iferr!=nil{returnnil,fmt.Errorf("failed to load Tigris config: %w",err)}// 2. Create an Amazon S3 client using the AWS Config instance created, "cfg"returns3.NewFromConfig(cfg,func(o*s3.Options){o.BaseEndpoint=aws.String("https://fly.storage.tigris.dev")o.Region="auto"}),nil}
For our Go Fly App, these environment variables should have already been set as secrets during the running of fly storage create:
Setting the following secrets on ...:
AWS_ACCESS_KEY_ID: <AWS_ACCESS_KEY_ID_VALUE>
AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
AWS_REGION: auto
AWS_SECRET_ACCESS_KEY: <AWS_SECRET_ACCESS_KEY>
BUCKET_NAME: <BUCKET_NAME>
Of course, this would only automatically happen if the create command was run in a directory containing a fly.toml for our Go Fly app.
But if that wasn’t the case, we can still manually set necessary secrets like so:
fly secrets set AWS_ACCESS_KEY_ID="<TIGRIS_ACCESS_KEY_ID>"
fly secrets set AWS_SECRET_ACCESS_KEY="<TIGRIS_SECRET_ACCESS_KEY>"
Upload a file: Aside from connecting with a Bucket, we can use other AWS SDK functions. Let’s use the PutObject function to upload a text file on the go like so: