Skip to main content

Services

This page contains practical examples for working with services in Dagger. Each section below provides code examples in multiple languages and demonstrates different approaches to service management.

Bind and use services in Dagger Functions

The first Dagger Function below creates and returns an HTTP service. This service is bound and used from a different Dagger Function, via a service binding using an alias like www.

package main

import (
"context"
"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Start and return an HTTP service
func (m *MyModule) HttpService() *dagger.Service {
return dag.Container().
From("python").
WithWorkdir("/srv").
WithNewFile("index.html", "Hello, world!").
WithExposedPort(8080).
AsService(dagger.ContainerAsServiceOpts{Args: []string{"python", "-m", "http.server", "8080"}})
}

// Send a request to an HTTP service and return the response
func (m *MyModule) Get(ctx context.Context) (string, error) {
return dag.Container().
From("alpine").
WithServiceBinding("www", m.HttpService()).
WithExec([]string{"wget", "-O-", "http://www:8080"}).
Stdout(ctx)
}

Example

Send a request from one Dagger Function to a bound HTTP service instantiated by a different Dagger Function:

dagger -c get

Expose services in Dagger Functions to the host

The Dagger Function below creates and returns an HTTP service. This service can be used from the host.

package main

import (
"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Start and return an HTTP service
func (m *MyModule) HttpService() *dagger.Service {
return dag.Container().
From("python").
WithWorkdir("/srv").
WithNewFile("index.html", "Hello, world!").
WithExposedPort(8080).
AsService(dagger.ContainerAsServiceOpts{Args: []string{"python", "-m", "http.server", "8080"}})
}

Examples

  • Expose the HTTP service instantiated by a Dagger Function to the host on the default port:
dagger -c 'http-service | up'

Access the service from the host:

curl localhost:8080
  • Expose the HTTP service instantiated by a Dagger Function to the host on a different host port:
dagger -c 'http-service | up --ports 9000:8080'

Access the service from the host:

curl localhost:9000

Expose host services to Dagger Functions

The following Dagger Function accepts a Service running on the host, binds it using an alias, and creates a client to access it via the service binding. This example uses a MariaDB database service running on host port 3306, aliased as db in the Dagger Function.

note

This implies that a service is already listening on a port on the host, out-of-band of Dagger.

package main

import (
"context"
"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Send a query to a MariaDB service and return the response
func (m *MyModule) UserList(
ctx context.Context,
// Host service
svc *dagger.Service,
) (string, error) {
return dag.Container().
From("mariadb:10.11.2").
WithServiceBinding("db", svc).
WithExec([]string{"/usr/bin/mysql", "--user=root", "--password=secret", "--host=db", "-e", "SELECT Host, User FROM mysql.user"}).
Stdout(ctx)
}

Example

Send a query to the database service listening on host port 3306 and return the result as a string:

dagger -c 'user-list tcp://localhost:3306'

Use service endpoints

The following Dagger Function starts a service manually, then retrieves its endpoint and sends a request. This example uses a NGINX HTTP service running on host port 80.

package main

import (
"context"
"dagger/my-module/internal/dagger"
)

type MyModule struct{}

func (m *MyModule) Get(ctx context.Context) (string, error) {
// Start NGINX service
service := dag.Container().From("nginx").WithExposedPort(80).AsService()
service, err := service.Start(ctx)
if err != nil {
return "", err
}

// Wait for service endpoint
endpoint, err := service.Endpoint(ctx, dagger.ServiceEndpointOpts{Scheme: "http", Port: 80})
if err != nil {
return "", err
}

// Send HTTP request to service endpoint
return dag.HTTP(endpoint).Contents(ctx)
}

Example

Send a query to the HTTP service listening on host port 80 and return the result as a string:

dagger -c get

Create a transient service for unit tests

The following Dagger Function creates a service and binds it to an application container for unit testing. In this example, the application being tested is Drupal. Drupal includes a large number of unit tests, including tests which depend on an active database service. This database service is created on-the-fly by the Dagger Function.

package main

import (
"context"

"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Run unit tests against a database service
func (m *MyModule) Test(ctx context.Context) (string, error) {
mariadb := dag.Container().
From("mariadb:10.11.2").
WithEnvVariable("MARIADB_USER", "user").
WithEnvVariable("MARIADB_PASSWORD", "password").
WithEnvVariable("MARIADB_DATABASE", "drupal").
WithEnvVariable("MARIADB_ROOT_PASSWORD", "root").
WithExposedPort(3306).
AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true})

// get Drupal base image
// install additional dependencies
drupal := dag.Container().
From("drupal:10.0.7-php8.2-fpm").
WithExec([]string{"composer", "require", "drupal/core-dev", "--dev", "--update-with-all-dependencies"})

// add service binding for MariaDB
// run kernel tests using PHPUnit
return drupal.
WithServiceBinding("db", mariadb).
WithEnvVariable("SIMPLETEST_DB", "mysql://user:password@db/drupal").
WithEnvVariable("SYMFONY_DEPRECATIONS_HELPER", "disabled").
WithWorkdir("/opt/drupal/web/core").
WithExec([]string{"../../vendor/bin/phpunit", "-v", "--group", "KernelTests"}).
Stdout(ctx)
}

Example

Run Drupal's unit tests, instantiating a database service during the process:

dagger -c test

Start and stop services

The following Dagger Function demonstrates how to control a service's lifecycle by explicitly starting and stopping a service. This example uses a Redis service.

package main

import (
"context"

"dagger/my-module/internal/dagger"
)

type MyModule struct{}

// Explicitly start and stop a Redis service
func (m *MyModule) RedisService(ctx context.Context) (string, error) {
redisSrv := dag.Container().
From("redis").
WithExposedPort(6379).
AsService(dagger.ContainerAsServiceOpts{UseEntrypoint: true})

// start Redis ahead of time so it stays up for the duration of the test
redisSrv, err := redisSrv.Start(ctx)
if err != nil {
return "", err
}

// stop the service when done
defer redisSrv.Stop(ctx)

// create Redis client container
redisCLI := dag.Container().
From("redis").
WithServiceBinding("redis-srv", redisSrv)

args := []string{"redis-cli", "-h", "redis-srv"}

// set value
setter, err := redisCLI.
WithExec(append(args, "set", "foo", "abc")).
Stdout(ctx)
if err != nil {
return "", err
}

// get value
getter, err := redisCLI.
WithExec(append(args, "get", "foo")).
Stdout(ctx)
if err != nil {
return "", err
}

return setter + getter, nil
}

Example

Start and stop a Redis service:

dagger -c redis-service