Migrate from Go 1.11 to the latest Go runtime

This page covers instructions for migrating from the first-generation to the second-generation Go runtimes. To upgrade your second-generation app to use the latest supported version of Go, see Upgrade an existing application.

Go 1.11 has reached end of support on January 30, 2024. Your existing Go 1.11 applications will continue to run and receive traffic. However, App Engine might block re-deployment of applications that use runtimes after their end of support date. We recommend that you migrate to the latest supported runtime of Go by using the guidelines in this page.

Migrating to a supported second-generation Go runtime lets you use up-to-date language features and build apps that are more portable, with idiomatic code.

Changes in the second-generation runtimes

Consider the following differences when upgrading to a supported second-generation Go runtime:

  • To reduce runtime migration effort and complexity, the App Engine standard environment lets you access many of the legacy bundled services and APIs in the second-generation runtimes, such as Memcache. Your second-generation Go app can call the bundled services APIs through the App Engine SDK for Go, and access most of the same features as on the Go 1.11 runtime.

    You also have the option to use Google Cloud products that offer similar features as the legacy bundled services. These Google Cloud products provide idiomatic Cloud Client Libraries for Go. For the bundled services that are not available as separate products in Google Cloud, such as image processing, search, and messaging, you can use third-party providers or other workarounds.

    To learn more about migrating to unbundled services, see Migrating from bundled services.

  • The behavior of some elements in the app.yaml configuration file has been modified. For more information, see Changes to the app.yaml file.

  • Logging in the second-generation runtime follows the logging standard in Cloud Logging. In the second-generation runtimes, app logs are no longer bundled with the request logs but are separated in different records. To learn more about reading and writing logs in the second-generation runtimes, see the logging guide.

Memory usage differences

Second-generation runtimes see a higher baseline of memory usage compared to first-generation runtimes. This is due to multiple factors, such as different base image versions, and differences in how the two generations calculate memory usage.

Second-generation runtimes calculate instance memory usage as the sum of what an application process uses, and the number of application files dynamically cached in memory. To avoid memory-intensive applications from experiencing instance shutdowns due to exceeding memory limits, upgrade to a larger instance class with more memory.

CPU usage differences

Second-generation runtimes can see a higher baseline of CPU usage upon instance cold-start. Depending on an application's scaling configuration, this might have unintended side effects, such as, a higher instance count than anticipated if an application is configured to scale based on CPU utilization. To avoid this issue, review and test application scaling configurations to ensure the number of instances are acceptable.

Request header differences

First-generation runtimes allow request headers with underscores (e.g. X-Test-Foo_bar) to be forwarded to the application. Second-generation runtimes introduces Nginx into the host architecture. As a result of this change, second-generation runtimes are configured to automatically remove headers with underscores (_). To prevent application issues, avoid using underscores in application request headers.

Changes to the app.yaml file

The behavior of some elements in the app.yaml configuration file has been modified:

Element Change type Description
app_engine_apis Required for apps using legacy bundled services Must be set to true if you want to access the legacy bundled services for the second-generation runtimes.
login Supported if app_engine_apis is true If you are not using the legacy bundled services for the second-generation runtimes, use these alternative methods to authenticate users.
runtime Modified Change the runtime element to specify a second-generation runtime.

For more information, see the app.yaml reference.

Creating a main package

Your service must include a package main statement in at least one source file. Alternatively, if your service is using the google.golang.org/appengine package, include a call to appengine.Main().

Writing a main package

If your service doesn't already contain a main package, add the package main statement and write a main() function. At a minimum, the main() function should:

  • Read the PORT environment variable and call the http.ListenAndServe() function:

    port := os.Getenv("PORT")
    if port == "" {
    	port = "8080"
    	log.Printf("Defaulting to port %s", port)
    }
    
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
    	log.Fatal(err)
    }

Registering your HTTP handlers

You can register your HTTP handlers by choosing one of the following options:

  • The preferred method is to manually move all http.HandleFunc() calls from your packages to your main() function in your main package.
  • Alternatively, import your application's packages into your main package, ensuring each init() function that contains calls to http.HandleFunc() gets run on startup.

    You can find all packages which use the http.HandleFunc() call with the following bash script, and copy the output into your main package's import block:

    gp=$(go env GOPATH) && p=$(pwd) && pkg=${p#"$gp/src/"} && find . -name "*.go" | xargs grep "http.HandleFunc" --files-with-matches | grep -v vendor/ | grep -v '/main.go' | sed "s#\./\(.*\)/[^/]\+\.go#\t_ \"$pkg/\1\"#" | sort | uniq
    

Structuring your files

Go requires each package has its own directory. You can tell App Engine where your main package is by using main: in your project's app.yaml file. For example, if your app's file structure looked like this:

myapp/
├── app.yaml
├── foo.go
├── bar.go
└── web/
    └── main.go

Your app.yaml file would have:

main: ./web # Relative filepath to the directory containing your main package.

For more information about the main flag, see the app.yaml reference.

Moving files to your GOPATH

Find your GOPATH by using the following command:

go env GOPATH

Move all relevant files and imports to your GOPATH. If using relative imports, such as import ./guestbook, update your imports to use the full path: import github.com/example/myapp/guestbook.