How to Use the /cmd Directory for Main Applications in Go Projects

The /cmd directory holds executable entry-points where each subdirectory corresponds to a binary name, containing only thin main wrappers that delegate to reusable packages in /internal and /pkg.

The golang-standards/project-layout repository defines community-accepted conventions for organizing Go codebases. When you use the /cmd directory for main applications, you create a clean separation between executable entry-points and reusable business logic, enabling better testability and code sharing across your organization.

Purpose of the /cmd Directory

According to the golang-standards/project-layout source code, the /cmd directory serves as the container for all executable entry-points (binaries) in your project. Each immediate subdirectory inside /cmd must be named after the binary you intend to produce—for example, cmd/myapp yields a myapp executable.

This structure originates from the repository's cmd/README.md and the main README.md /cmd section, which document that the directory should contain minimal code.

Structuring Binary Subdirectories

Naming Conventions

Name each subdirectory after the resulting binary. For a server named api and a CLI tool named cli, create:


cmd/
  api/
    main.go
  cli/
    main.go

The Thin Wrapper Pattern

The main.go file inside each binary directory should act as a thin wrapper. According to the project layout standards, it contains only the main function responsible for wiring together dependencies and invoking core logic defined elsewhere.

Separating Concerns with /internal and /pkg

To maximize reusability while enforcing boundaries, the /cmd directory imports from two critical locations:

/internal – Private application code that cannot be imported by external projects. The Go compiler enforces this boundary automatically. Place your business logic here, such as internal/app/service.go.

/pkg – Public library code intended for consumption by other repositories. External projects may import these packages, as seen in pkg/util/helpers.go.

By keeping application logic out of /cmd, you enable reuse in other programs, simplify testing without binary invocation, and maintain clear architectural boundaries.

Complete Project Layout Example

The following structure demonstrates a minimal yet complete implementation following the golang-standards/project-layout conventions:


project/
├─ cmd/
│  └─ myapp/
│     └─ main.go            <-- thin wrapper
├─ internal/
│  └─ app/
│     └─ service.go        <-- private business logic
└─ pkg/
   └─ util/
      └─ helpers.go        <-- reusable utilities

cmd/myapp/main.go

The entry-point delegates all work to imported packages:

package main

import (
	"log"

	"github.com/yourusername/project/internal/app"
	"github.com/yourusername/project/pkg/util"
)

func main() {
	// Initialize reusable utilities
	if err := util.Setup(); err != nil {
		log.Fatalf("setup failed: %v", err)
	}

	// Run the core service (private code)
	if err := app.Run(); err != nil {
		log.Fatalf("service error: %v", err)
	}
}

internal/app/service.go

Contains the main application logic, protected from external import:

package app

import "fmt"

// Run contains the main application logic.
// It is private to this repository.
func Run() error {
	fmt.Println("Hello from the internal service!")
	// ... more business logic ...
	return nil
}

pkg/util/helpers.go

Provides reusable utilities that external projects may import:

package util

import "fmt"

// Setup provides a reusable helper that can be imported by any project.
func Setup() error {
	fmt.Println("Utility setup complete.")
	return nil
}

Building Executables from /cmd

To compile the binary while preserving the project structure, specify the path to the subdirectory containing main.go:

go build -o bin/myapp ./cmd/myapp

Executing bin/myapp produces:


Utility setup complete.
Hello from the internal service!

This approach ensures that go build treats cmd/myapp as a command package, creating a standalone executable while keeping your workspace organized.

Summary

  • The /cmd directory contains executable entry-points, with each subdirectory named after the target binary
  • main.go files should remain thin wrappers that import logic from /internal and /pkg
  • Use /internal for private code that the Go compiler protects from external import
  • Use /pkg for public libraries intended for reuse across projects
  • Build specific binaries using go build ./cmd/<binary-name>

Frequently Asked Questions

What belongs inside the /cmd directory versus the /pkg directory?

The /cmd directory should contain only main packages and minimal wiring code required to start an application. All reusable business logic, domain models, and service implementations belong in /internal (if private) or /pkg (if public). This separation ensures that your executables remain lightweight and that core logic can be tested independently without invoking a binary.

Can I have multiple binaries in a single Go project?

Yes. The golang-standards/project-layout explicitly supports multiple binaries by placing each in its own subdirectory under /cmd. For example, you might have cmd/server for a daemon process and cmd/migrate for database migrations. Each subdirectory contains its own main.go and builds independently using go build ./cmd/server or go build ./cmd/migrate.

How does the /internal directory enforce privacy?

Go's compiler recognizes the internal directory as a special path. Code within internal/ and its subdirectories can only be imported by packages rooted at the parent of the internal directory. For instance, code in project/internal/app/ can be imported by project/cmd/myapp but not by an external repository, preventing accidental coupling to your implementation details.

Should I place all my main.go files directly in /cmd or in subdirectories?

Always place main.go files inside subdirectories named after the desired binary. The /cmd directory itself should not contain Go source files directly. Following cmd/<binary-name>/main.go ensures that go build produces correctly named executables and maintains clarity about which code produces which binary.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →