Sign In
Sign In

How to Install and Configure Go on Ubuntu: A Step-by-step Guide

How to Install and Configure Go on Ubuntu: A Step-by-step Guide
Anees Asghar
Technical writer
Go
18.10.2024
Reading time: 6 min

Go, also known as Golang, is a programming language created by Google. It simplifies coding and provides great performance, which makes it popular among developers. Its user-friendly syntax and built-in concurrency support allow developers to efficiently create scalable applications. Moreover, Go is a cross-platform programming language and is available for all major operating systems, including Linux, Windows, etc.

In this article, we’ll discuss what Golang is and how to install and use it on Ubuntu.

What is Golang?

Golang is a simple yet powerful programming language that enables developers to build basic to advanced applications. Go was developed to manage large-scale projects, with a focus on concurrency and clean code. Its scalability and cross-platform compatibility have made it a popular choice among developers.

Go is widely used in systems programming, networking, and microservices. Key benefits of Golang include fast compilation, simple syntax, automatic memory management, and tools like go fmt and go test.

Prerequisites for Installing Go on Ubuntu

To install the Go language on Ubuntu, the below-listed requirements must be fulfilled:

  • Ubuntu must be pre-installed.
  • A user with root or sudo privileges.

If a user fulfills the prerequisites, he can proceed with the installation of Go on Ubuntu.

Method 1: Installing Golang on Ubuntu via the Default Package Manager

Apt is the default package manager of Ubuntu. Installing Golang through the default package is the most convenient way. However, it’s important to note that the version installed via Ubuntu's default package manager may not always be the latest.

Let’s go through the below-listed steps to install Go on Ubuntu through apt:

Updating and Upgrading System Repositories

Before installing Golang on Ubuntu, it is recommended to update and upgrade the system repositories to ensure we have the latest package information:

sudo apt update && sudo apt upgrade -y

Image1

Installing Go Using apt

Now run the command below to install Go on Ubuntu using the default package manager:

sudo apt install golang-go -y

Image3

The installation process for Golang on Ubuntu may take a while to complete. 

Verify Go Language Installation

Finally, confirm the Golang installation on Ubuntu by checking its version:

go version

The output indicates that the go version go1.22.2 has been installed successfully:

Image2

Uninstall Go from Ubuntu

If Golang is no longer needed, uninstall it from Ubuntu by executing the following command:

sudo apt remove golang-go -y

Image5

It's recommended to remove unnecessary dependent packages, as they can take up extra space. To do this, simply execute the below-given command:

sudo apt autoremove

Image4

Method 2: Installing Golang Using wget

To install the Go programming language on Ubuntu from the official source, users can use the wget command. This command downloads the latest Golang binary package from the official website. 

Let’s go through the step-by-step process below to download and install Golang on Ubuntu using the wget command.

Download and Install Go Language

First, open a browser and navigate to the Go language’s official All Releases page. Scroll down to select an appropriate binary package under the stable version. Copy the link address of the selected binary package:

Image7

Now specify the copied link address with the wget command, as shown below:

wget https://go.dev/dl/go1.23.2.linux-amd64.tar.gz -O golang.tar.gz

Here, the -O option is used to specify an output file name. For instance, the above command downloads the Golang binary package and saves it as golang.tar.gz:

Image6

After this, use the tar command to extract the downloaded package:

sudo tar -xzvf golang.tar.gz -C /usr/local

Image9

Set Up the Go Environment

Configuring the PATH variable for Golang allows access to Go commands and tools from any directory on the system. For this purpose, the Go binary paths need to be added to the PATH environment variable:

echo export PATH=$HOME/golang/bin:/usr/local/go/bin:$PATH >> ~/.profile

Image8

Next, execute the following command to apply the changes made to the profile file:

source ~/.profile

Image12

Verify Go Installation

Once the Go environment is configured, execute the command below to check if Go has been successfully installed:

go version

Image10

Configure the $GOPATH and $GOROOT (Optional)

In Golang, the default workspace directory is ~/go, and the environment variable $GOPATH refers to this location. However, users can customize this workspace by setting the $GOPATH environment variable in their ~/.profile, ~/.bashrc, or ~/.zshrc files:

export GOPATH=$HOME/golangExamples

This command will set the workspace directory to ~/golangExamples. Moreover, users can also set $GOROOT to override the default Go installation path. However, this is usually not necessary:

export GOROOT=/usr/local/go

Finally, execute the command below to apply the changes:

source ~/.profile

Users can customize the $GOPATH and $GOROOT environment variables, however, it is not recommended to change them unless you have a specific reason.

Remove Go From Ubuntu

If Golang is downloaded using the wget command and now needs to be removed, simply run the command below:

sudo rm -rf /usr/local/go

This command will permanently remove the Go language from Ubuntu. 

How to Use Go Programming Language on Ubuntu?

Once the Go programming language is installed on Ubuntu, we can use it to fulfill our programming needs, such as creating applications, and web services, handling concurrent tasks, etc. 

You can use any text editor, like nano, to create a new file and save it with a .go extension:

nano exampleCode.go

After creating a go file, paste the following code into it to print a "Welcome to hostman.com" message on the terminal:

package main

import "fmt"

func main() {
    fmt.Println("Welcome to hostman.com")
}

Save the code, close the nano editor, and then run the command below in the terminal to execute the Go program:

go run exampleCode.go

The output shows that the "Welcome to hostman.com" message is successfully displayed in the Ubuntu terminal, which confirms that the Golang program has been executed correctly:

Image11

That’s all about installing, configuring, and using the Go programming language on Ubuntu.

Conclusion

Golang is a cross-platform programming language best known for its easy-to-use syntax, concurrency support, and scalability. Key benefits of Golang include fast compilation, simple syntax, and built-in tools like go fmt and go test, etc. These features make Golang a perfect choice for building efficient applications. In this article, we explored two methods to install Golang on Ubuntu. The first method uses the default package manager (apt). The second method involves downloading via wget. Choose any of the discussed methods to install the Go language and start using it to develop scalable applications on Ubuntu.

Go
18.10.2024
Reading time: 6 min

Similar

Go

Multithreading in Golang

Single-threaded applications in Golang look like ordinary, sequentially executing code. In this case, all invoked functions are executed one after the other, passing the return value from the completed function as an argument to the next one. There are no shared data, issues with concurrent access (reading and writing), or synchronization. Multithreaded Go applications parallelize the logic into several parts, speeding up program execution. In this case, the tasks are performed simultaneously. In this article, we will create the logic for a simple single-threaded Go application and then modify the code to turn it into a multithreaded one. Simple Application Let's create a basic scenario where we have multiple mines, and inside, mining for ore takes place. In the code below, we have two caves, each containing a unique set of resources. Each cave has a mining progress state that indicates the number of digs performed inside the mine: package main import ( "fmt" // for console output "time" // for creating timeouts ) func mining(name string, progress *int, mine *[]string) { // using pointers to track the mining progress and mine contents if *progress < len(*mine) { // checking if the mining progress is less than the mine size time.Sleep(2 * time.Second) // pause execution for 2 seconds, simulating the mining process fmt.Printf("In mine «%s», found: «%s»\n", name, (*mine)[*progress]) // print the found resource and mine name to the console (notice how we dereference the pointer to the array) *progress++ // increment the mine’s progress mining(name, progress, mine) // repeat the mining process } } func main() { mine1 := []string{"stone", "iron", "gold", "stone", "gold"} // Mine #1 mine1Progress := 0 // Mining progress for mine #1 mine2 := []string{"stone", "stone", "iron", "stone"} // Mine #2 mine2Progress := 0 // Mining progress for mine #2 mining("Stonefield", &mine1Progress, &mine1) // start mining Mine #1 mining("Rockvale", &mine2Progress, &mine2) // start mining Mine #2 } In the example above, the mines are worked on one after another until completely exhausted. Therefore, the console output will strictly follow this sequence: In mine «Stonefield», found: «stone» In mine «Stonefield», found: «iron» In mine «Stonefield», found: «gold» In mine «Stonefield», found: «stone» In mine «Stonefield», found: «gold» In mine «Rockvale», found: «stone» In mine «Rockvale», found: «stone» In mine «Rockvale», found: «iron» In mine «Rockvale», found: «stone» Notice that Stonefield is completely mined first, followed by Rockvale. This sequential (single-threaded) mining process seems quite slow and inefficient. You could assume that the reason is a lack of necessary equipment. If there is only one mining drill, you can't mine both caves simultaneously,  only one after the other. In theory, we could optimize mining so that multiple drills work at the same time, turning resource extraction into a multithreaded process. Let's try doing that. Goroutines You can parallelize the execution of several tasks using what is called "goroutines" in Golang. A "goroutine" is essentially a function that doesn't block the execution of the code that follows it when it starts running. Calling such a parallel function is simple – you just need to add the keyword go before the function call. func main() { // these functions will execute sequentially action() action() action() // these functions will start executing simultaneously right after they are called go anotherAction() // "go" is specified, so the code will continue without waiting for the function's results go anotherAction() go anotherAction() } Now we can slightly modify our mining application: package main import ( "fmt" "time" ) func mining(name string, progress *int, mine *[]string) { if *progress < len(*mine) { time.Sleep(2 * time.Second) fmt.Printf("In mine «%s», found: «%s»\n", name, (*mine)[*progress]) *progress++ mining(name, progress, mine) } } func main() { mine1 := []string{"stone", "iron", "gold", "stone", "gold"} mine1Progress := 0 mine2 := []string{"stone", "stone", "iron", "stone"} mine2Progress := 0 go mining("Stonefield", &mine1Progress, &mine1) // added the "go" keyword go mining("Rockvale", &mine2Progress, &mine2) // added "go" here as well for mine1Progress < len(mine1) && mine2Progress < len(mine2) { // loop runs until mining progress in each mine matches its size fmt.Printf("Supply Center is waiting for miners to return...\n") time.Sleep(3 * time.Second) // execute the code inside the loop every 3 seconds, printing a message from the "Supply Center" } } The console output from this code will differ, as the mining results will be interspersed: Supply Center is waiting for miners to return... In mine «Rockvale», found: «stone» In mine «Stonefield», found: «stone» Supply Center is waiting for miners to return... In mine «Stonefield», found: «iron» In mine «Rockvale», found: «stone» Supply Center is waiting for miners to return... In mine «Rockvale», found: «iron» In mine «Stonefield», found: «gold» In mine «Stonefield», found: «stone» In mine «Rockvale», found: «stone» Supply Center is waiting for miners to return... In mine «Stonefield», found: «gold» As you can see, mining in both caves is happening simultaneously, and the information about resource extraction is interspersed with messages from the "Supply Center," which is periodically produced by the main program loop. However, to implement multithreading in real Golang applications, goroutines alone are not enough. Therefore, we will look at a few more concepts. Channels Channels are like "cables" that allow goroutines to communicate and exchange information with each other. This provides a special way to pass data between tasks running in different threads. Symbols like arrows (<-) are used to send and receive data in channels. Here’s an example: package main import "fmt" func main() { someChannel := make(chan string) // Create a channel go func() { // Create a self-invoking function to send a message to the channel fmt.Println("Waiting for 2 seconds...") time.Sleep(2 * time.Second) someChannel <- "A message" // Send data to the channel }() message := <-someChannel // The execution pauses here until a message is received from the channel fmt.Println(message) } Console output: Waiting for 2 seconds...A message However, in this example, you can only send one message into the channel. To send multiple values, you need to specify the channel size explicitly: package main import ( "fmt" "time" ) func main() { someChannel := make(chan string, 2) // Create a buffered channel go func() { fmt.Println("Waiting for 2 seconds...") time.Sleep(2 * time.Second) someChannel <- "A message" fmt.Println("Waiting another 2 seconds...") time.Sleep(2 * time.Second) someChannel <- "Another message" }() message1 := <-someChannel fmt.Println(message1) message2 := <-someChannel fmt.Println(message2) } Console output: Waiting for 2 seconds...Waiting another 2 seconds...A messageAnother message This is an example of blocking synchronization using goroutines and channels. Channel Directions Channels can be directional, meaning you can create a channel only for sending or only for receiving data, increasing type safety. For example, a channel can be both readable and writable, but you can pass it to functions with restrictions on how it can be used. One function may only be allowed to write to the channel, while another can only read from it: package main import "fmt" // This function only sends data to the channel func write(actions chan<- string, name string) { actions <- name } // This function only reads data from the channel func read(actions <-chan string, execution *string) { *execution = <-actions } func main() { actions := make(chan string, 3) // Buffered channel with a size of 3 var execution string write(actions, "Read a book") write(actions, "Clean the house") write(actions, "Cook dinner") read(actions, &execution) fmt.Printf("Current task: %s\n", execution) read(actions, &execution) fmt.Printf("Current task: %s\n", execution) read(actions, &execution) fmt.Printf("Current task: %s\n", execution) } Console output: Current task: Read a bookCurrent task: Clean the houseCurrent task: Cook dinner Non-blocking Channel Reads You can use a select statement to avoid blocking when reading from a channel: package main import ( "fmt" "time" ) func main() { channel := make(chan string) go func() { // Self-invoking goroutine channel <- "Message received\n" }() // First select will hit the default section since the message hasn't arrived yet select { case message := <-channel: fmt.Println(message) default: fmt.Println("No messages") } time.Sleep(2 * time.Second) // Wait for 2 seconds // Second select will now receive the message from the channel select { case message := <-channel: fmt.Println(message) default: fmt.Println("No messages") } } Refined Application Now that we know how to use goroutines and channels, let’s modify the previous mining application. In this scenario, we will have a "Supply Center" that launches the mining process for all available mines. Once the mining is done, each mine will notify the Supply Center that it's finished, and the Supply Center will then terminate the program. In the following code, we create separate structures for the mines and the Supply Center: package main import ( "fmt" "time" ) type Mine struct { name string // Mine name resources []string // Resources in the mine progress int // Mining progress finished chan bool // Channel for signaling the completion of mining } type SupplyCenter struct { mines []*Mine // Array of pointers to all the existing mines } func dig(m *Mine) { if m.progress < len(m.resources) { time.Sleep(1 * time.Second) fmt.Printf("In mine \"%s\", found: \"%s\"\n", m.name, m.resources[m.progress]) m.progress++ dig(m) } else { m.finished <- true // Send a completion signal to the channel } } func main() { supply := SupplyCenter{[]*Mine{ {"Stonefield", []string{"stone", "iron", "gold", "stone", "gold"}, 0, make(chan bool)}, {"Rockvale", []string{"stone", "stone", "iron", "stone"}, 0, make(chan bool)}, {"Ironridge", []string{"iron", "gold", "stone", "iron", "stone", "gold"}, 0, make(chan bool)}, }} // Start the mining process for all created mines for _, mine := range supply.mines { go dig(mine) } // Wait for completion signals from all mines for _, mine := range supply.mines { <-mine.finished } // Once all mines are done, the program terminates } Sample output: In mine "Rockvale", found: "stone" In mine "Ironridge", found: "iron" In mine "Stonefield", found: "stone" In mine "Ironridge", found: "gold" In mine "Stonefield", found: "iron" In mine "Rockvale", found: "stone" In mine "Ironridge", found: "stone" In mine "Rockvale", found: "iron" In mine "Stonefield", found: "gold" In mine "Rockvale", found: "stone" In mine "Stonefield", found: "stone" In mine "Ironridge", found: "iron" In mine "Ironridge", found: "stone" In mine "Stonefield", found: "gold" In mine "Ironridge", found: "gold" You can verify that all resources were mined by counting the number of lines in the output. It will match the total number of resources in all the mines. Conclusion The examples in this tutorial are simplified, but they demonstrate the power of concurrency in Golang. Goroutines and channels provide flexible ways to manage concurrent tasks in real-world applications. It’s important to follow some basic principles to avoid complicating your program's logic: Prefer channels over shared variables (or pointers) for synchronization between goroutines. Choose appropriate language constructs to "wrap" concurrency primitives. Avoid unnecessary blocking and ensure proper scheduling of procedures. Use profiling tools (like the net/http/pprof package in Go) to identify bottlenecks and optimize performance when developing multithreaded applications.
30 September 2024 · 11 min to read
Go

Data Types in Go

Go (Golang) is a multi-threaded programming language developed by Google in 2009. Its syntax is similar to that of the C language, and it is designed to make it easier to write highly efficient software. In this tutorial, we'll look at the different Golang data types and give recommendations for choosing them. Prerequisites A machine with the Go language installed. You can run Go on your local computer or on a cloud server on Hostman which can be securely managed via SSH without the need for logins and passwords. Declaring Variables Declaring a variable is the process of creating and assigning it a name and data type. In Go, variable declaration is done with the var keyword, as well as using the := operator. The syntax for variables declaration in Go using var is: var variable_name data_type And this is what declaring variables through var looks like: var FirstName stringvar salary float32 Now, let's look at the second way of declaring variables, namely with initialization, i.e., indicating the initial value when creating them. The syntax for declaring variables with initialization is as follows: var variable_name data_type = value This is what it looks like: var FirstName string = "Mike"var salary float32 = 70000 In addition to the above, there is a short way to declare variables using the := operator. This method automatically sets the type of the variable when specifying the value. Syntax: variable_name := initial_value Examples: FirstName := "Mike"salary := 70000 In the output, you will see that the FirstName variable is automatically assigned the string type, and the salary variable is automatically assigned the int type. Type string for MikeType int for 70000...Program finished with exit code 0Press ENTER to exit console. Deploy and manage your apps with ease Basic Data Types First of all, let's look at the main Golang data types. Integer data types Integer data types in Go describe integer values, which can be positive, negative, or zero. There are several types of integer data in Go, which differ in byte size. Let's look at each of them in more detail in the table below. Type Description Range int8 8-bit signed integers.  from -128 to 127 int16 16-bit signed integers.  from -32,768 to 32,767 int32 32-bit signed integers.  from -231 to 231-1 int64 64-bit signed integers.  from -263 to 263-1 int 32-bit or 64-bit signed integers (depending on the platform). Used by default with integer values. As int32 or int64 (depending on the platform) uint8 8-bit unsigned integers. from 0 to 255 uint16 16-bit unsigned integers. from 0 to 65,535 uint32 32-bit unsigned integers. from 0 to 232-1 uint64 64-bit unsigned integers. from 0 to 264-1 uint 32-bit or 64-bit unsigned integers (depending on the platform). As uint32 or uint64 (depending on the platform) In addition to those listed, there are also byte and rune. They are equivalent to uint8 and int32, respectively. Examples of declaring integer variables: var x int16 = 45000var y uint = 73000var z byte = 4 Real data types Real data types are used to store fractional numbers. Go offers two options: float32 and float64. float32 takes 4 bytes in memory and can store floating point numbers from -3.4028235E+38 to 3.4028235E+38 with a precision of 7 decimal places; float64 occupies 8 bytes in memory and can store floating point numbers from -1.7976931348623157E+308 to 1.7976931348623157E+308 with a precision of 15 decimal places. It is important to understand that real numbers in a computer are represented in binary form, and therefore may be inaccurate. This may result in rounding errors when performing operations on such numbers. Therefore, when working with them, it is important to take into account the peculiarities of their representation in a computer and take measures to avoid rounding errors. Examples of declaring real variables: var x float32 = 3.14var y float64 = 3.1415926535897 String data types String data types in Go are used to store character strings. Each character in a string represents a sequence of bytes in memory. Strings are immutable objects in Go. This means that once a string is created, its contents cannot be changed. However, you can create a new string by combining several existing strings. Go also has special characters. Some of them are: \n - line feed or newline \t - tab \" is a double quote \' is a single quote \\ is a backslash In addition, it is possible to use Unicode characters. To do this, you can use a sequence of characters in the \uXXXX format, where XXXX is the hexadecimal value of the character's Unicode. Examples of declaring a string variable: var exampleText1 string = "Hello, user1!"exampleText2 := "Hello, user2!" Boolean data type Go has a boolean data type, bool, which can take the values true and false. This data type can be used to store the values of logical expressions, for example in conditional if and switch statements, as well as in logical operators such as && (logical AND), || (logical OR) and ! (logical NOT). For example, we can use the boolean data type in the following code: age := 21isAdult := age >= 21fmt.Println(isAdult) When executing the code, the output will show true. Composite data types Next, let's look at composite types in Go. Arrays Arrays in Go are used to store a fixed number of elements of the same type. The main difference from other languages is that here arrays are values, not references to data. To define an array, you must specify the element type and the number of elements in square brackets. For example: var exampleArr [4]int = [4]int{2, 5, 7, 9} Array elements are accessed by index, starting from 0: fmt.Println(exampleArr[0]) Arrays in Go also support iteration using a for loop: for i := 0; i < len(exampleArr); i++ {     fmt.Println(exampleArr[i])} Slices Slices are a reference data type in Golang that represents a dynamic array of elements stored in memory. They are very similar to arrays, but unlike them, the slices' size can be adjusted during program execution, and they are references to data rather than values. A slice is defined using the following syntax: var slice []T Where T is the type of slice elements, and slice is a variable that stores a reference to the slice. An empty slice can be declared like this: emptySlice := []int{} To initialize a slice, you can use the make function, which creates a new slice of a given length: slice := make([]int, 5) The example above creates a slice of five int elements. In addition, the make function has a third optional parameter that denotes capacity. It is used to pre-specify the amount of memory that will be allocated to store slice elements. Due to the reduction in the number of memory allocation operations, the load on the processor is significantly reduced. For example: slice := make([]int, 10, 20) This code snippet creates a slice containing 10 elements of the int data type and with a pre-allocated capacity of 20 elements. This means that the slice initially contains 10 zero-initialized elements, and can be expanded to a maximum of 20 elements without the need to allocate additional memory. If suddenly the number of slice elements becomes more than 20, then Go will automatically allocate a new memory block that will be 2 times larger than the previous one. The capacity approach is recommended when the user knows in advance the maximum number of elements that will need to be stored in the slice. Indexing is used to access elements, just like with arrays. Structures In short, structures are a collection of fields. The fields, in turn, characterize various attributes of objects. To declare a structure in Go, you use the keyword type, followed by the name of the structure and its definition in curly braces. For example, we will declare a structure representing information about a person: type Person struct {     Name string     Age int     Address string} To create a variable that stores the values of a structure, you need to use the var keyword and the name of the variable. The variable name is then followed by the structure name, followed by curly braces with the structure field values in the form field_name: value. For example: var person1 Personperson1 = Person{Name: "John", Age: 30, Address: "123 Main St."} Here we declare a variable person1 of the Person type and assign it values for each field. You can also use a short form of structure initialization: person2 := Person{Name: "Jane", Age: 25, Address: "456 Elm St."} Structure fields are accessed using the dot (.) operator. For example, to get the name person1, you need to write: person1.Name Structures can contain other structures within themselves and even references to themselves (so-called recursive structures). In addition, they can implement interfaces. Maps A map is a reference data type that is used to represent an unordered set of key-value pairs. The keys in the map must be unique, but the values may be repeated. To create a map, the built-in make() function is used, which is passed the key and value types. The following code creates an empty map with string keys and int values: m := make(map[string]int) To add an element to a map, you can use the following entry form: m[key] = value For example: m["apple"] = 3m["banana"] = 5 To get the value of an element by key, use the following notation form: value := m[key] For example, to get the value of an element with the key apple: count := m["apple"] If an element with the specified key is not in the map, the default value will be returned. To remove an element from a map, you can use the built-in delete() function. The following code removes the element with the key apple: delete(m, "apple") In Go, maps are implemented as hash tables, so the order of elements in a map is not fixed and can change each time the program is executed. You should also pay attention to the fact that the map is not thread-safe, therefore, when accessing the map in parallel, you must use access synchronization mechanisms (mutexes or channels). Pointers and Interfaces Pointers  Pointers are needed to interact with variables that store addresses in memory. They can be defined by using the & symbol in front of the variable, and the * symbol is used to get the value pointed to by the pointer. Example: var myInt int = 42var myIntPointer *int = &myIntfmt.Println(*myIntPointer) The output of the program is shown in the picture below. Interfaces Interfaces are used to define a set of methods that a data type must implement. Interfaces can be used to abstract away a specific data type and to implement polymorphism. To create an interface in Go, you need to define a set of methods that the data type must implement. Example: type Writer interface {     Write([]byte) (int, error)} This defines the Writer interface, which must have a Write method with an argument of type []byte and return the number of bytes written and an error. Safe and scalable Virtual Servers and VPC Conclusion In this tutorial, we looked at the main data types in Golang. In addition, we have provided declaration syntax and working examples for each of them. We hope that this guide and tips will help you learn how to choose the right data types in your code, leading to improved performance of the programs you develop.
10 April 2024 · 9 min to read
Go

Installing Go on CentOS

CentOS is an open-source operating system based on the source code of the Red Hat Enterprise Linux project. At its core, CentOS is based on the Linux kernel. Novice users may need clarification about the CentOS versioning since Red Hat produces two distributions of this operating system, CentOS Linux and CentOS Stream. So, we will first look at their differences and then describe how to install Go on CentOS.  CentOS distributions CentOS is originally based on the commercial Red Hat Enterprise Linux (RHEL) distribution but differs from it in that it is distributed as free software and has no official support. For this reason, it is sometimes assumed that RHEL is used as a corporate product in large companies, while small development teams prefer CentOS. During the entire existence of CentOS, nine versions have been released, the last of which represented a separate branch: CentOS Stream. The thing is, when the company released version 8 of CentOS, most developers were still using version 7. Around the same time, Red Hat began the process of merging with IBM and decided to stop supporting version 8 in 2021. However, because of the widespread use of version 7, Red Hat decided to continue releasing critical updates until 2024. It was version 8 that was followed by CentOS Stream in 2019, which is released on a rolling-release model. In other words, updates for this distribution are released regularly and continuously. The latest version of CentOS is still numbered 9 (that is, the next version after 8), but instead of "Linux", "Stream" is specified. At the same time, the Stream branch is also in version 8 of CentOS. In this article, we will look at installing the Golang programming language on the latest versions of CentOS which are 8 and 9, but you can also use this guide to install Go on CentOS 7. 1. Uninstall old versions of Golang Check for older versions To avoid possible problems during installation, you must ensure that Golang is not present on your system. There are several ways to do this. You can try to request the Go version through a special command: go version If Go is indeed already installed, the console will display a message with the Golang version. Another option, which can also help with uninstallation, is to find out the location of the directory with Go that you want to uninstall later: which go If Golang already exists on the system, the path to the directory will appear in the console. Uninstall Go If Golang is indeed already installed, then it needs to be removed: rm -rf /usr/local/go If the which go command showed another directory, specify it in the command above. However, it is usually /usr/local/go. The rm command deletes directories and files, while the -rf flag indicates the recursive-forced type of deletion. r stands for recursive, which means it deletes the specified folder, all its subfolders, subfolders of subfolders, etc. f stands for force, meaning no external conditions or variables can prevent the deletion. Clear environment variables To remove Go from the environment variables, you need to locate one of the following files: /etc/profile ~/.profile In the file, remove the lines that point to the Go directory. Usually, the lines are these:  export PATH=$PATH:/usr/local/go/binexport GOROOT=$HOME/go1.Xexport PATH=$PATH:$GOROOT/bin If any of the above lines are present in the file, delete them. Now you can proceed to downloading Golang. Managed solution for Backend development 2. Download Golang Go to the official Golang website. To download the compiler, you can use wget or curl. We will use the first option, directly pointing the link to the archive we need: wget https://dl.google.com/go/go1.21.3.linux-amd64.tar.gz Note that the link specifies the exact version of Go. You can find the number of the latest version for the Linux kernel-based operating systems on the Downloads page. The wget command launches a Linux utility that downloads files from the Internet via a command line terminal. It works with all major protocols: HTTP, HTTPS, and FTP. After executing this command, the archive will be downloaded. In our case, it is go1.21.3.linux-amd64.tar.gz; you may have a different version.  After downloading the archive, check its checksum and make sure that the hash displayed in the command line matches the hash specified on the download page: sha256sum go1.21.3.linux-amd64.tar.gz Note that the .tar.gz extension indicates the compressed archive file format, which is a combination of two other archive formats: TAR. Used for storing multiple files in a single archive GZIP. Used to compress the data in an archive to reduce its size After you download the archive, you will need to extract it.  3. Extract the archive For extraction, use the tar utility: sudo tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz When extracting, specify the default directory /usr/local. This is where the Golang compiler will be placed. The extract command should be run as root or a sudo user. Now, we need to tell the system where the compiler is located. To do this, we will set the environment variables. 4. Set environment variables In order for the command line terminal to know where to look for Go executables when we compile a program, we need to specify the address of the Golang directory in the environment variables. To do this, open the /etc/profile file and add the following lines to it: export PATH=$PATH:/usr/local/go/binexport GOROOT=/usr/local/goexport GOPATH=$HOME/Documents/go We have set 3 environment variables: PATH. Helps the command line find the compiler binaries GOROOT. Points to the directory where Go is installed GOPATH. Contains the address of the working directory For the changes to apply, you can either restart the command line terminal or enter the updated data manually with a special command: source /etc/profile 5. Check the installation Now, let's check that Golang was installed correctly and can be accessed from the console terminal. To do this, use the command: go version The terminal should display the message with Golang version number. That's it. We have installed Go on CentOS. 6. Compiling a simple program We will try running a simple application to make sure that everything works as it should. Let's create a new file in the home directory: nano main.go Then fill it with the following contents: package main import "fmt" func main() { fmt.Println("Hello, World!") // CONCLUSION: Hello, World! } Now let's run our program using the run command: go run main.go To create a full-fledged executable file, we need another command: go build If you do not specify a file name as an argument, the command will compile main.go. However, you can explicitly specify the name of the file to be built into the final "build": go build filename.go Without explicitly specifying a filename in the build command, an error may occur due to the way Go's module system works: go: go.mod file not found in current directory or any parent directory; see 'go help modules' There are two ways to solve this error. One is to manually initialize go.mod file in the working directory of the project, specifying the name of the source file: go mod init filename.go Another is to change the value of the GO111MODULE environment variable to auto: go env -w GO111MODULE=auto Or to off: go env -w GO111MODULE=off The build itself adds the system-dependent auxiliary code needed to run the application on any computer with the same system architecture to the executable. Conclusion Since CentOS is based on the Linux kernel, installing Golang is the same process for all recent versions of the operating system: 7, 8, 9 (Stream). You can learn more about the language syntax in the official Golang documentation.
22 February 2024 · 6 min to read

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start.
Email us
Hostman's Support