How to generate a UUID in Go
Go has no UUID type in its standard library, so you add one dependency: github.com/google/uuid, where uuid.New() returns a random v4 and uuid.NewV7() returns a time-ordered v7. Every snippet below was run on Go 1.22 with google/uuid v1.6.0.
By the Withuse team · Updated
The one-liner
import "github.com/google/uuid" id := uuid.New() id.String() // "d0c4eafc-df14-48b3-b373-d8c9d6328491" id.Version() // VERSION_4 id.Variant() // RFC4122
Choosing a version
Every version with google/uuid
uuid.New() // v4, panics on rand failure
uuid.NewRandom() // v4, returns (UUID, error)
uuid.NewV7() // v7, returns (UUID, error)
uuid.NewSHA1(uuid.NameSpaceDNS, []byte("example.com"))
// v5 -> cfbff0d1-9375-5685-968c-48ce8b15ae17
uuid.NewMD5(uuid.NameSpaceDNS, []byte("example.com"))
// v3
uuid.NewUUID() // v1, returns (UUID, error)The v5 value above is identical to what Python and JavaScript produce for the same namespace and name — verified across all three. That cross-language agreement is the whole point of the name-based versions.
Trap 1: New() panics, NewRandom() does not
// Panics if crypto/rand fails — fine in main, risky inside a library.
id := uuid.New()
// Explicit: you decide what a failed random source means.
id, err := uuid.NewRandom()
if err != nil {
return fmt.Errorf("generate id: %w", err)
}
// Panic on purpose, visible at the call site.
id := uuid.Must(uuid.NewRandom())A failing crypto/rand usually means the process is beyond saving, so New() is reasonable in application code. Avoid it in a library, where a panic crosses an API boundary the caller did not opt into.
Trap 2: the zero value is a valid Nil UUID
var id uuid.UUID // no error, no nil
fmt.Println(id) // 00000000-0000-0000-0000-000000000000
fmt.Println(id == uuid.Nil) // true
// Guard explicitly where "unset" is a real state:
if order.ID == uuid.Nil {
return errors.New("order id is unset")
}uuid.UUID is a [16]byte array, so it has a zero value rather than a nil one. An unpopulated struct field, a JSON payload missing the key, or a scanned NULL column all silently become the Nil UUID. When absent and zero genuinely differ in your domain, use a *uuid.UUID field.
UUID v7 and its timestamp
v7, _ := uuid.NewV7() // 01a041d3-3631-7b71-b418-a2a466963cbb sec, nsec := v7.Time().UnixTime() time.Unix(sec, nsec).UTC() // 2026-08-27T06:05:56.145Z — the creation time
Values generated inside the same millisecond still sort correctly: google/uuid increments a counter in the random field rather than redrawing it, which we confirmed by generating three in a tight loop and checking their string order. That property is what makes v7 useful as a primary key — see UUID v4 vs v7 for the index reasoning.
Library comparison
| google/uuid | gofrs/uuid | |
|---|---|---|
| Versions | v1, v3, v4, v5, v6, v7 | v1, v3, v4, v5, v6, v7 |
| Panicking constructor | Yes — uuid.New() | No — every constructor returns an error |
| Typical reason to pick it | Default; often already in your module graph | Error-only API; migrating off satori |
| Zero value | uuid.Nil | uuid.Nil |
Avoid satori/go.uuid: it is unmaintained and once shipped an incompatible API change under the same import path, which broke builds that had not pinned a version.
Storing UUIDs with pgx
// PostgreSQL: use the native uuid column type (16 bytes, not 36 chars).
_, err := pool.Exec(ctx,
"INSERT INTO orders (id, total) VALUES ($1, $2)",
uuid.Must(uuid.NewV7()), total)
// Scanning back
var id uuid.UUID
err = pool.QueryRow(ctx, "SELECT id FROM orders WHERE ...").Scan(&id)pgx handles uuid.UUID natively. With database/sql, google/uuid already implements sql.Scanner and driver.Valuer, so the type works there too. Column-type guidance lives in the PostgreSQL guide.
Frequently asked questions
Which UUID library should I use in Go?
The standard library has no UUID package, so you need a dependency, and google/uuid is the default choice for most teams: it is small, stable at v1.6.0, ships v1 through v7, and appears in the dependency tree of many popular modules already, so adding it often costs nothing extra in your build. The main alternative, gofrs/uuid, is the maintained continuation of the older satori/go.uuid package and is equally sound. Prefer it if you want an API that never panics — every constructor returns an error — or if you are migrating code that already imports satori. What you should not use is satori/go.uuid itself, which has been unmaintained for years and once shipped a breaking API change under the same import path.
What is the difference between uuid.New() and uuid.NewRandom()?
They generate the same value; they differ in how they handle failure. NewRandom() returns (UUID, error) and lets you decide what to do if the system random source is unavailable. New() calls it internally and panics on error, which is the convenience most code wants because a failing crypto/rand almost always means the process is unrecoverable. The trap is using New() in a library or a request handler where a panic crosses an API boundary you do not control. In that position prefer NewRandom() and handle the error, or wrap explicitly with uuid.Must(uuid.NewRandom()) so the panic is visible at the call site rather than hidden inside a helper.
How do I generate UUID v7 in Go?
Call uuid.NewV7(), which returns (UUID, error) and has been available in google/uuid since v1.5.0. The value carries a 48-bit Unix millisecond timestamp in its leading bits, so identifiers sort by creation time and behave well as database primary keys. Verified on google/uuid v1.6.0, the implementation also maintains monotonicity inside a single millisecond: three values generated back to back in the same millisecond still sort in creation order, because the library increments a counter in the random field rather than redrawing it. You can read the timestamp back with u.Time().UnixTime(), which returns seconds and nanoseconds suitable for time.Unix().
Why is my Go UUID all zeros?
Because you are looking at a zero-value uuid.UUID, which is a [16]byte array and therefore has a valid zero state rather than a nil one. Declaring var id uuid.UUID without assigning gives you 00000000-0000-0000-0000-000000000000, the Nil UUID, and Go will not complain — it is a legitimate value, not an error. This bites most often with struct fields that were never populated, JSON payloads missing the identifier key, and database scans of a NULL column. Guard against it explicitly by comparing against uuid.Nil before using the value, and consider a *uuid.UUID pointer field when absent and zero genuinely mean different things in your domain.
How do I store UUIDs in PostgreSQL from Go?
Use the native uuid column type and let the driver handle the conversion. pgx recognises uuid.UUID directly, so you can pass the value to Query or Exec with no wrapper, and it stores 16 bytes rather than the 36 characters a text column would need. With database/sql and lib/pq, pass the value's String() form or implement the Scanner and Valuer interfaces, which google/uuid already provides. For the primary key itself, prefer uuid.NewV7() over uuid.New(): time-ordered values keep inserts appending to the right-hand edge of the B-tree index instead of scattering across it, which matters as the table grows.
References
Need sample UUIDs for a Go test fixture? Generate up to 1,000 at once with our free UUID generator. Also in this series: UUID in Java · collision probability