secret
kr / go.md
Last active

Go Buildpack Quickstart

  • Download Gist
go.md
Markdown

This quickstart will get you going with Go on Heroku.

Prerequisites

Local Workstation Setup

We'll start by setting up your local workstation with the Heroku command-line client and the Git revision control system; and then logging into Heroku to upload your ssh public key. If you've used Heroku before and already have a working local setup, skip to the next section.

See the Heroku Toolbelt site for installation.

Once installed, you'll have access to the heroku command from your shell. Log in using the email address and password you used when creating your Heroku account:

$ heroku login
Enter your Heroku credentials.
Email: kr@example.com
Password: 
Could not find an existing public key.
Would you like to generate one? [Yn] 
Generating new SSH public key.
Uploading ssh public key /Users/kr/.ssh/id_rsa.pub

Press enter at the prompt to upload your existing ssh key or create a new one, used for pushing code later on.

Installing Go

It's easiest to install Go from one of the official Go packages provided by the Go team.

See the Go getting started page for full installation instructions and How to Write Go Code to learn how best to set up your local environment, including what values to use for environment variables and which directories to use. Here's a summary.

Make a directory to hold all your Go source:

$ mkdir -p $HOME/gocode/src

The go tools use env var GOPATH to find source code to build on your system, so put the following in your $HOME/.profile:

GOPATH=$HOME/gocode export GOPATH

And run

$ . $HOME/.profile

to pick up this change in your current shell.

Write Your Application

Now it’s time to create a simple “hello, world” application. If you'd like to skip to the end, you can clone the sample app at https://github.com/kr/go-heroku-example.

First, make a directory to hold your new Heroku app, and initialize an empty git repository there:

$ mkdir -p $GOPATH/src/example.com/server
$ cd $GOPATH/src/example.com/server
$ git init

Here’s the actual application code:

app.go

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", hello)
    err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

func hello(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(w, "hello, world!")
}

Build your App and its Dependencies

The Go buildpack recognizes Go apps by the existence of a .go source file anywhere in the repository.

Your app and its dependencies are built all together by the go command-line tool.

You can build your app using go get:

$ go get

Declare Process Types With Foreman and Procfile

To run your web process, you need to declare what command to use. In this case, we simply need to execute our Go program. We’ll use Procfile to declare how our web process type is run.

Foreman is documented in the foreman man page and Heroku's Procfile support in a separate devcenter article.

Here's a Procfile for the sample app we've been working on:

web: server

Now that you have a Procfile, you can start your application with Foreman:

$ foreman start
13:58:29     web.1  | started with pid 5997

Your app will come up on port 5000. Test that it's working with curl or a web browser, then Ctrl-C to exit.

Set a Hint for the Go Buildpack

The go tool uses the directory name of your project to name executables and determine package import paths, but the Go buildpack doesn't have direct access to this information, so you have to tell it. Create a file called .godir, in your project root, containing the import path for the package in the root of your repo, in this case example.com/server. Command go list, with no arguments, prints the import path for the current package:

$ go list >.godir

Store Your App in Git

We now have the three pieces of our app: application source in app.go, process types in Procfile, and the path to the project. Let's put it into Git:

$ git add .
$ git commit -m init

Deploy to Heroku

Create the app and tell it to use the Go buildpack:

$ heroku create --buildpack git://github.com/kr/heroku-buildpack-go.git
Creating pure-sunrise-3607... done, stack is cedar
BUILDPACK_URL=git://github.com/kr/heroku-buildpack-go.git
http://pure-sunrise-3607.herokuapp.com/ | git@heroku.com:pure-sunrise-3607.git
Git remote heroku added

Deploy your code:

$ git push heroku master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 687 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Fetching custom git buildpack... done
-----> Go app detected
-----> Installing Go 1.0.2... done
       Installing Virtualenv... done
       Installing Mercurial... done
       Installing Bazaar... done
-----> Running: go get ./...
-----> Discovering process types
       Procfile declares types -> web, worker
-----> Compiled slug size is 1.8MB
-----> Launching... done, v1
       http://pure-sunrise-3607.herokuapp.com deployed to Heroku
To git@heroku.com:pure-sunrise-3607.git
 * [new branch]      master -> master    

Now, let's check the state of the app's processes:

$ heroku ps
=== web: `server`
web.1: up for 15s

The web process is up. Review the logs for more information:

$ heroku logs
2012-08-03T01:17:15+00:00 heroku[api]: Deploy db538ce by kr@example.com
2012-08-03T01:17:15+00:00 heroku[api]: Release v4 created by kr@example.com
2012-08-03T01:17:16+00:00 heroku[slugc]: Slug compilation finished
2012-08-03T01:17:19+00:00 heroku[web.1]: Starting process with command `server`
2012-08-03T01:17:20+00:00 heroku[web.1]: State changed from starting to up

Looks good. We can now visit the app with heroku open.

Running a Worker

The Procfile format lets you run any number of different process types. For example, let's say you wanted a worker process to complement your web process. Just add another directory containing the source code for your worker command, work/work.go:

package main

import (
    "time"
    "fmt"
)

func Worker() {
    for {
        fmt.Println("Working")
        time.Sleep(time.Second)
    }
}

Build as usual with the go tool:

$ go get ./...

And add the worker command to your Procfile:

web: server
worker: work

(Running more than one dyno for an extended period may incur charges to your account. Read more about dyno-hour costs.)

Push this change to Heroku, then launch a worker:

$ git push heroku master
$ heroku scale worker=1
Scaling worker processes... done, now running 1

Check heroku ps to see that your worker comes up:

$ heroku ps
=== web: `server`
web.1: up for 10s

=== worker: `work`
worker.1: up for 9s

Watch the worker in real time by tailing the logs:

$ heroku logs --tail
2012-09-17T23:01:19+00:00 heroku[worker.1]: Starting process with command `work`
2012-09-17T23:01:20+00:00 heroku[worker.1]: State changed from starting to up
2012-09-17T23:01:20+00:00 app[worker.1]: Working
2012-09-17T23:01:21+00:00 app[worker.1]: Working
2012-09-17T23:01:22+00:00 app[worker.1]: Working

The content of Procfile should be(right?):
web: ./bin/hello

see:
line 91-92 of https://github.com/kr/heroku-buildpack-go/blob/master/bin/compile

The Procfile is intended to be, as shown,

web: hello

PATH contains /app/bin, although that's not necessarily obvious from
looking at the buildpack, unless you are familiar with how the buildpack
interface works. The code to set PATH is in
https://github.com/kr/heroku-buildpack-go/blob/master/bin/release

Thanks very much, that's very helpful

Fixed a typo (buidpack -> buildpack) and changed the name of "hello" to "server" to make the distinction between web and worker more clear (https://gist.github.com/0dfbaf4b65e747299992). Any idea of when one of these will make it onto the heroku support site?

Oh, also: deploy worked fine but starting foreman tells me that "hello" (or whatever you call the web process) is not found. Any ideas?

@nickoneill thanks for the patches. If foreman isn't finding the
command, make sure that $GOPATH/bin is in your $PATH

@kr Thanks, that worked.

Nice work, thanks.

Needed to change BUILDPACK_URL for it to work for me:

heroku config:add BUILDPACK_URL=git://github.com/kr/heroku-buildpack-go.git

Also, setting the port and running the app directly works, as well as deploying to heroku, but I still can't get foreman to work (adding $GOPATH/bin to $PATH didn't help, although this is just a minor issue)

Thanks for the guide! I've made a couple changes on my fork:

  • Added example output from heroku logs
  • Removed some references to Cedar. "Heroku is Cedar now" and so on.

@mattonrails thanks! Merged.

Am I missing a step in the worker instructions? I have this directory structure:

    $GOPATH/src/service
    $GOPATH/src/service/worker

Main web server is in service/, worker is in worker/. I can do
go get ./worker
and it builds worker just fine, but my Procfile doesn't seem to see it, I always get "bash: worker: command not found"

    web: service
    worker: worker

Nevermind, I'm just terrible and broke it :)

Hi,

When I try to 'git push heroku master', I keep getting:

-----> Heroku receiving push
-----> Fetching custom buildpack... cloning with git...done
-----> Go app detected
-----> ERROR: Please create .godir
-----> See https://gist.github.com/299535bbf56bf3016cba for instructions
 !     Heroku push rejected, failed to compile Go app

But I do have a .godir in the root of my application. I can cat the file, my application builds with 'go install' and, yes, I've commited the '.godir'. Directory structure is:
$GOPATH=$HOME/gocode
Project is at: $GOPATH/src/project
Files (main.go & .godir) at: $GOPATH/src/project/.godir

Any ideas?

Disregard my post above, that was my mistake, I was pushing the wrong branch to heroku (one without .godir).

Works great - thanks!

Note that if you don't do heroku ps:scale web=1 this won't work

@kr could you also add documentation on how to connect to pg on Heroku? Thanks

Example, followed exactly, results in an app that boots, but immediately returns "Application Error"

$ pwd
/Users/example/go/src/example.com/test
~/go/src/example.com/test 
$ ls -a
.        ..       .git     .godir   Procfile app.go
~/go/src/example.com/test 
$ cat .godir 
example.com/test
~/go/src/example.com/test 
$ cat Procfile 
web: test
~/go/src/example.com/test 
$ cat app.go 
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/", hello)
    err := http.ListenAndServe(":"+os.Getenv("PORT"), nil)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    }
}

func hello(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintln(w, "hello, world!")
}~/go/src/example.com/test 
$ heroku ps
=== web: `test`
web.1: crashed 2013/02/19 15:47:25 (~ 2m ago)

The requirement to adhere to the go convention is onerous considering go get will compile independent of using $GOPATH.

An example project that actually works and can be forked would be extremely helpful.

Thanks for the suggestion! Here's a sample app:
https://github.com/kr/go-heroku-example

Hopefully this will help you while debugging. Let me
know if you still have any problems. :)

I think the reason your app doesn't boot is that you named
the command test, which happens to be the same as a
command provided by the OS. The OS test command
comes first in the search path, so when the dyno boots
it's running the wrong binary.

I'm unable to figure this out. According to http://golang.org/doc/code.html my project layout should be like so:

  • src/
  • bin/
  • pkg/

This I have done in my slightly more that trivial coin flipping project.
All is well and I'm able to run locally (GOPATH set to project root). However, when I push to heroku I get a couple of errors:

<snip>
-----> Running: go get -tags heroku ./...
package coinflips/database: unrecognized import path "coinflips/database"

and two more like it.
The one thing I noticed is that my project root does not have a .go file in it. Surely this is sane? I, for example, do not want to place a settings file next to my source, nor view templates.
I've twiddled a bit with the .godir contents to no avail.

Could someone nudge me in the right direction?

@haarts your project should go inside the src/ directory, and GOPATH normally doesn't point at the project root.

For example, for a project called coinflips, you might have the following layout:

~/go/
    src/
        github.com/
            haarts/
                coinflips/
                    .git/
                    .godir
                    coinflips.go
                    …

with the contents of .godir as github.com/haarts/coinflips and with GOPATH=~/go

@kr In that structure, where would I put a settings file or the aforementioned view template files?
The subpackages would go in ~/go/src/github.com/haarts/coinflips/database am I right?

@haarts you can put things anywhere you like within your repo.
If you prefer not to have any go source files in the repo root, you
can put all of them in subpackages, like
~/go/src/github.com/haarts/coinflips/web and, as you mentioned,
~/go/src/github.com/haarts/coinflips/database.

You can put any other, non-source-code files wherever you want
as well, presumably inside the repo. You could make a subdirectory
or put the template files in the project root.

Note that import paths are relative to the src/ directory in the Go workspace,
so in this example you'd import "github.com/haarts/coinflips/database".

I'm pretty sure I'm going to get this to work! Thanks a boat load.

I am using a few external libraries but I am getting problems with the go get on the build, I dont get this on my local machine so I am not sure how to deal with the issues as I dont use or reference there examples directories:

-----> Fetching custom git buildpack... done
-----> Go app detected
-----> Installing Go 1.0.3... done
       Installing Virtualenv... done
       Installing Mercurial... done
       Installing Bazaar... done
-----> Running: go get -tags heroku ./...
# code.google.com/p/log4go/examples
code.google.com/p/log4go/examples/FileLogWriter_Manual.go:17: main redeclared in this block
    previous declaration at code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go:9
code.google.com/p/log4go/examples/SimpleNetLogServer.go:21: main redeclared in this block
    previous declaration at code.google.com/p/log4go/examples/FileLogWriter_Manual.go:17
code.google.com/p/log4go/examples/SimpleNetLogServer.go:25: not enough arguments in call to net.ResolveUDPAddr
code.google.com/p/log4go/examples/SocketLogWriter_Manual.go:9: main redeclared in this block
    previous declaration at code.google.com/p/log4go/examples/SimpleNetLogServer.go:21
code.google.com/p/log4go/examples/XMLConfigurationExample.go:5: main redeclared in this block
    previous declaration at code.google.com/p/log4go/examples/SocketLogWriter_Manual.go:9
# github.com/ziutek/mymysql/examples
github.com/ziutek/mymysql/examples/prepared_stmt.go:10: printOK redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/long_data.go:14
github.com/ziutek/mymysql/examples/prepared_stmt.go:14: checkError redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/long_data.go:18
github.com/ziutek/mymysql/examples/prepared_stmt.go:26: main redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/long_data.go:25
github.com/ziutek/mymysql/examples/reconnect.go:11: main redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/prepared_stmt.go:26
github.com/ziutek/mymysql/examples/simple.go:10: printOK redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/prepared_stmt.go:10
github.com/ziutek/mymysql/examples/simple.go:14: checkError redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/prepared_stmt.go:14
github.com/ziutek/mymysql/examples/simple.go:22: checkedResult redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/prepared_stmt.go:21
github.com/ziutek/mymysql/examples/simple.go:27: main redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/reconnect.go:11
github.com/ziutek/mymysql/examples/transactions.go:11: printOK redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/simple.go:10
github.com/ziutek/mymysql/examples/transactions.go:15: checkError redeclared in this block
    previous declaration at github.com/ziutek/mymysql/examples/simple.go:14
github.com/ziutek/mymysql/examples/transactions.go:15: too many errors
 !     Heroku push rejected, failed to compile Go app

@ollietrex what happens when you run go get -u ./... locally?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.