Skip to main content
This guide walks you through adding a new Go microservice using the new-service command and manual configuration steps.

Prerequisites

  • Development environment set up with direnv allow
  • Basic understanding of Go and Protocol Buffers
  • Docker Compose or Tilt running (for testing)

Using the new-service Command

The new-service command scaffolds a new Go microservice with all the necessary boilerplate.
1

Generate the service scaffold

Run the command with your service name and optional port:
new-service go <service-name> [port]
Examples:
# Generate a service on default port 8080
new-service go my-service

# Generate a service on custom port 8085
new-service go payment-service 8085
Service names must be in kebab-case (e.g., my-service, not MyService or my_service).
This creates:
  • services/cmd/<service-name>/main.go - Entry point
  • services/internal/<service-name>/ - Service implementation directory
  • deploy/docker/<service-name>/Dockerfile.dev - Development Dockerfile
  • deploy/k8s/<service-name>.nix - Kubernetes manifest module
  • proto/<service-name>/v1/<service-name>.proto - Proto definition
2

Implement your service logic

Add your service implementation in services/internal/<service-name>/service.go.Example structure:
package myservice

import (
    "context"
    "connectrpc.com/connect"
    myservicev1 "github.com/hackz-megalo-cup/microservices-app/services/gen/go/myservice/v1"
)

type Service struct{}

func NewService() *Service {
    return &Service{}
}

func (s *Service) YourRPC(
    ctx context.Context,
    req *connect.Request[myservicev1.YourRequest],
) (*connect.Response[myservicev1.YourResponse], error) {
    // Implementation here
    return connect.NewResponse(&myservicev1.YourResponse{
        Message: "Hello from my-service",
    }), nil
}
Refer to existing services like services/internal/greeter/service.go for complete examples.
3

Define your proto schema

Edit the generated proto file at proto/<service-name>/v1/<service-name>.proto to define your service contract:
syntax = "proto3";

package myservice.v1;

option go_package = "github.com/hackz-megalo-cup/microservices-app/services/gen/go/myservice/v1;myservicev1";

service MyServiceService {
  rpc YourRPC(YourRequest) returns (YourResponse) {}
}

message YourRequest {
  string name = 1;
}

message YourResponse {
  string message = 1;
}
Then regenerate code:
buf generate
4

Add to docker-compose.yml

Add your service to docker-compose.yml following the pattern of existing services:
my-service:
  build:
    context: .
    dockerfile: deploy/docker/my-service/Dockerfile.dev
  environment:
    PORT: "8085"
    OTEL_EXPORTER_OTLP_ENDPOINT: ""
    OTEL_SERVICE_NAME: "my-service"
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.my-service.rule=PathPrefix(`/myservice.v1.MyServiceService`)"
    - "traefik.http.routers.my-service.entrypoints=web"
    - "traefik.http.routers.my-service.priority=100"
    - "traefik.http.routers.my-service.middlewares=cors@file,rate-limit@file"
    - "traefik.http.services.my-service.loadbalancer.server.port=8085"
    - "traefik.http.services.my-service.loadbalancer.server.scheme=h2c"
  networks:
    - app
Make sure the PathPrefix in the Traefik rule matches your proto package and service name exactly.
5

Add to Kubernetes manifests (nixidy)

Add the import to deploy/nixidy/env/local.nix:
imports = [
  # ... existing imports
  ../../k8s/my-service.nix
];
6

Update Tiltfile

Add your service to the Tiltfile in multiple places:1. Add to gen-manifests deps:
local_resource(
    'gen-manifests',
    cmd='bash scripts/gen-manifests.sh',
    deps=[
        # ... existing deps
        'deploy/k8s/my-service.nix',
    ],
    # ...
)
2. Add manifest collection:
manifests += find_yaml('deploy/manifests/my-service')
3. Add go_service call:
go_service('my-service', 'cmd/my-service')
4. Add k8s_resource configuration:
if manifests:
    # ... existing resources
    
    my_service_deps = cluster_bootstrap_deps + ['gen-manifests', 'buf-generate']
    if not use_nix:
        my_service_deps += ['my-service-compile']
    k8s_resource('my-service', port_forwards=8085, resource_deps=my_service_deps)
7

Git add new files (CRITICAL for Nix)

CRITICAL: Nix can only reference files that are tracked in the git tree. You must run git add before generating manifests.
git add services/cmd/my-service/
git add services/internal/my-service/
git add deploy/docker/my-service/
git add deploy/k8s/my-service.nix
git add proto/my-service/
Without this step, gen-manifests will fail with “file not found” errors.
8

Generate manifests and test

Generate Kubernetes manifests:
gen-manifests
Start your services:
# Using Docker Compose
docker compose up my-service

# Using Tilt
tilt up
Test the service:
# List available RPCs
grpcurl -plaintext localhost:8085 list

# Call your RPC
grpcurl -plaintext -d '{"name":"World"}' localhost:8085 myservice.v1.MyServiceService/YourRPC

Next Steps

  • Add unit tests in services/internal/<service-name>/service_test.go
  • Implement observability with OpenTelemetry traces
  • Add integration tests if calling other services
  • Update frontend to consume your new service

Common Issues

You forgot to git add the new files. Nix requires all files to be in the git tree.
git add deploy/k8s/my-service.nix
git add proto/my-service/
Check your proto syntax:
buf lint
Ensure option go_package is correctly set in your .proto file.
Verify the PathPrefix in docker-compose.yml matches your proto package exactly:
  • Proto: package myservice.v1;
  • Service: service MyServiceService
  • PathPrefix: /myservice.v1.MyServiceService

See Also

Build docs developers (and LLMs) love