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