Service
The Service type represents a content-addressed service providing TCP connectivity.
Common operations​
Some of the common operations you can perform with a Service include:
| Field | Description | 
|---|---|
endpoint | Returns a URL or host:port pair to reach the service | 
hostname | Returns a hostname to reach the service | 
ports | Returns the list of ports provided by the service | 
up | Creates a tunnel that forwards traffic from the caller's network to the service | 
Examples​
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.
- Go
 - Python
 - TypeScript
 - PHP
 
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)
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    def http_service(self) -> dagger.Service:
        """Start and return an HTTP service."""
        return (
            dag.container()
            .from_("python")
            .with_workdir("/srv")
            .with_new_file("index.html", "Hello, world!")
            .with_exposed_port(8080)
            .as_service(args=["python", "-m", "http.server", "8080"])
        )
    @function
    async def get(self) -> str:
        """Send a request to an HTTP service and return the response."""
        return await (
            dag.container()
            .from_("alpine")
            .with_service_binding("www", self.http_service())
            .with_exec(["wget", "-O-", "http://www:8080"])
            .stdout()
        )
import { dag, object, func, Service } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Start and return an HTTP service
   */
  @func()
  httpService(): Service {
    return dag
      .container()
      .from("python")
      .withWorkdir("/srv")
      .withNewFile("index.html", "Hello, world!")
      .withExposedPort(8080)
      .asService({ args: ["python", "-m", "http.server", "8080"] })
  }
  /**
   * Send a request to an HTTP service and return the response
   */
  @func()
  async get(): Promise<string> {
    return await dag
      .container()
      .from("alpine")
      .withServiceBinding("www", this.httpService())
      .withExec(["wget", "-O-", "http://www:8080"])
      .stdout()
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\{DaggerObject, DaggerFunction, Doc};
use Dagger\Service;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Start and return an HTTP service')]
    public function httpService(): Service
    {
        return dag()
            ->container()
            ->from('python')
            ->withWorkdir('/srv')
            ->withNewFile('index.html', 'Hello, world!')
            ->withExposedPort(8080)
            ->asService(args: ['python', '-m', 'http.server', '8080']);
    }
    #[DaggerFunction]
    #[Doc('Send a request to an HTTP service and return the response')]
    public function get(): string
    {
        return dag()
            ->container()
            ->from('alpine')
            ->withServiceBinding('www', $this->httpService())
            ->withExec(['wget', '-O-', 'http://www:8080'])
            ->stdout();
    }
}
Example​
Send a request from one Dagger Function to a bound HTTP service instantiated by a different Dagger Function:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c get
get
dagger call 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.
- Go
 - Python
 - TypeScript
 - PHP
 
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"}})
}
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    def http_service(self) -> dagger.Service:
        """Start and return an HTTP service."""
        return (
            dag.container()
            .from_("python")
            .with_workdir("/srv")
            .with_new_file("index.html", "Hello, world!")
            .with_exposed_port(8080)
            .as_service(args=["python", "-m", "http.server", "8080"])
        )
import { dag, object, func, Service } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Start and return an HTTP service
   */
  @func()
  httpService(): Service {
    return dag
      .container()
      .from("python")
      .withWorkdir("/srv")
      .withNewFile("index.html", "Hello, world!")
      .withExposedPort(8080)
      .asService({ args: ["python", "-m", "http.server", "8080"] })
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\{DaggerObject, DaggerFunction, Doc};
use Dagger\Service;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Start and return an HTTP service')]
    public function httpService(): Service
    {
        return dag()
            ->container()
            ->from('python')
            ->withWorkdir('/srv')
            ->withNewFile('index.html', 'Hello, world!')
            ->withExposedPort(8080)
            ->asService(args: ['python', '-m', 'http.server', '8080']);
    }
}
Examples​
- Expose the HTTP service instantiated by a Dagger Function to the host on the default port:
 
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'http-service | up'
http-service | up
dagger call 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:
 
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'http-service | up --ports 9000:8080'
http-service | up --ports 9000:8080
dagger call 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.
This implies that a service is already listening on a port on the host, out-of-band of Dagger.
- Go
 - Python
 - TypeScript
 - PHP
 
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)
}
from typing import Annotated
import dagger
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
    @function
    async def user_list(
        self, svc: Annotated[dagger.Service, Doc("Host service")]
    ) -> str:
        """Send a query to a MariaDB service and return the response."""
        return await (
            dag.container()
            .from_("mariadb:10.11.2")
            .with_service_binding("db", svc)
            .with_exec(
                [
                    "/usr/bin/mysql",
                    "--user=root",
                    "--password=secret",
                    "--host=db",
                    "-e",
                    "SELECT Host, User FROM mysql.user",
                ]
            )
            .stdout()
        )
import { dag, object, func, Service } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Send a query to a MariaDB service and return the response
   */
  @func()
  async userList(
    /**
     * Host service
     */
    svc: Service,
  ): Promise<string> {
    return await dag
      .container()
      .from("mariadb:10.11.2")
      .withServiceBinding("db", svc)
      .withExec([
        "/usr/bin/mysql",
        "--user=root",
        "--password=secret",
        "--host=db",
        "-e",
        "SELECT Host, User FROM mysql.user",
      ])
      .stdout()
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\{DaggerObject, DaggerFunction, Doc};
use Dagger\Service;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Send a query to a MariaDB service and return the response')]
    public function userList(
        #[Doc('host service')]
        Service $svc,
    ): string {
        return dag()
            ->container()
            ->from('mariadb:10.11.2')
            ->withServiceBinding('db', $svc)
            ->withExec([
                '/usr/bin/mysql',
                '--user=root',
                '--password=secret',
                '--host=db',
                '-e',
                'SELECT Host, User FROM mysql.user',
            ])
            ->stdout();
    }
}
Example​
Send a query to the database service listening on host port 3306 and return the result as a string:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c 'user-list tcp://localhost:3306'
user-list tcp://localhost:3306
dagger call user-list --svc=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.
- Go
 - Python
 - TypeScript
 - PHP
 
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)
}
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    async def get(self) -> str:
        # start NGINX service
        service = dag.container().from_("nginx").with_exposed_port(80).as_service()
        await service.start()
        # wait for service endpoint
        endpoint = await service.endpoint(port=80, scheme="http")
        # s end HTTP request to service endpoint
        return await dag.http(endpoint).contents()
import { dag, object, func } from "@dagger.io/dagger"
@object()
export class MyModule {
  @func()
  async get(): Promise<string> {
    // start NGINX service
    let service = dag.container().from("nginx").withExposedPort(80).asService()
    service = await service.start()
    // wait for service to be ready
    const endpoint = await service.endpoint({ port: 80, scheme: "http" })
    // send HTTP request to service endpoint
    return await dag.http(endpoint).contents()
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\{DaggerObject, DaggerFunction, Doc};
use Dagger\Service;
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    public function get(): string
    {
        // start NGINX service
        $service = dag()->container()->from('nginx')->withExposedPort(80)->asService();
        $service->start();
        // wait for service to be ready
        $endpoint = $service->endpoint(80, 'http');
        // send HTTP request to service endpoint
        return dag()->http($endpoint)->contents();
    }
}
Example​
Send a query to the HTTP service listening on host port 80 and return the result as a string:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c get
get
dagger call 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.
- Go
 - Python
 - TypeScript
 - PHP
 
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)
}
from dagger import dag, function, object_type
@object_type
class MyModule:
    @function
    async def test(self) -> str:
        """Run unit tests against a database service."""
        # get MariaDB base image
        mariadb = (
            dag.container()
            .from_("mariadb:10.11.2")
            .with_env_variable("MARIADB_USER", "user")
            .with_env_variable("MARIADB_PASSWORD", "password")
            .with_env_variable("MARIADB_DATABASE", "drupal")
            .with_env_variable("MARIADB_ROOT_PASSWORD", "root")
            .with_exposed_port(3306)
            .as_service(use_entrypoint=True)
        )
        # get Drupal base image
        # install additional dependencies
        drupal = (
            dag.container()
            .from_("drupal:10.0.7-php8.2-fpm")
            .with_exec(
                [
                    "composer",
                    "require",
                    "drupal/core-dev",
                    "--dev",
                    "--update-with-all-dependencies",
                ]
            )
        )
        # add service binding for MariaDB
        # run kernel tests using PHPUnit
        return await (
            drupal.with_service_binding("db", mariadb)
            .with_env_variable("SIMPLETEST_DB", "mysql://user:password@db/drupal")
            .with_env_variable("SYMFONY_DEPRECATIONS_HELPER", "disabled")
            .with_workdir("/opt/drupal/web/core")
            .with_exec(["../../vendor/bin/phpunit", "-v", "--group", "KernelTests"])
            .stdout()
        )
import { dag, object, func } from "@dagger.io/dagger"
@object()
class MyModule {
  /**
   * Run unit tests against a database service
   */
  @func()
  async test(): Promise<string> {
    const 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({ useEntrypoint: true })
    // get Drupal base image
    // install additional dependencies
    const drupal = dag
      .container()
      .from("drupal:10.0.7-php8.2-fpm")
      .withExec([
        "composer",
        "require",
        "drupal/core-dev",
        "--dev",
        "--update-with-all-dependencies",
      ])
    // add service binding for MariaDB
    // run kernel tests using PHPUnit
    return await drupal
      .withServiceBinding("db", mariadb)
      .withEnvVariable("SIMPLETEST_DB", "mysql://user:password@db/drupal")
      .withEnvVariable("SYMFONY_DEPRECATIONS_HELPER", "disabled")
      .withWorkdir("/opt/drupal/web/core")
      .withExec(["../../vendor/bin/phpunit", "-v", "--group", "KernelTests"])
      .stdout()
  }
}
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\{DaggerObject, DaggerFunction, Doc};
use function Dagger\dag;
#[DaggerObject]
class MyModule
{
    #[DaggerFunction]
    #[Doc('Run unit tests against a database service')]
    public function test(): string
    {
        $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(useEntrypoint: true);
        // get Drupal base image
        // install additional dependencies
        $drupal = dag()
            ->from('drupal:10.0.7-php8.2-fpm')
            ->withExec([
                '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(['../../vendor/bin/phpunit', '-v', '--group', 'KernelTests'])
            ->stdout();
    }
}
Example​
Run Drupal's unit tests, instantiating a database service during the process:
- System shell
 - Dagger Shell
 - Dagger CLI
 
dagger -c test
test
dagger call test