Get started with Testcontainers for Zig in 5 minutes.
- Zig 0.15.2 installed
- Docker or Docker Desktop installed and running
Add to your build.zig.zon:
.dependencies = .{
.testcontainers = .{
.url = "git+https://github.com/dragosv/testcontainers-zig?ref=main#<commit>",
.hash = "<hash>",
},
},Wire it up in build.zig:
const tc_dep = b.dependency("testcontainers", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("testcontainers", tc_dep.module("testcontainers"));const std = @import("std");
const tc = @import("testcontainers");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
// Start a PostgreSQL container using the built-in module.
var provider = try tc.DockerProvider.init(alloc);
defer provider.deinit();
const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{
.username = "user",
.password = "password",
.database = "mydb",
});
defer pg.terminate() catch {};
defer pg.deinit();
const url = try pg.connectionString(alloc);
defer alloc.free(url);
std.debug.print("Connected to: {s}\n", .{url});
}const ctr = try tc.run(alloc, "nginx:latest", .{
.exposed_ports = &.{"80/tcp"},
.wait_strategy = tc.wait.forHttp("/"),
});
defer ctr.terminate() catch {};
defer ctr.deinit();
const port = try ctr.mappedPort("80/tcp", alloc);
std.debug.print("nginx running on port {d}\n", .{port});const std = @import("std");
const testing = std.testing;
const tc = @import("testcontainers");
test "postgres: connection string is non-empty" {
const alloc = testing.allocator;
var provider = tc.DockerProvider.init(alloc) catch return error.SkipZigTest;
defer provider.deinit();
const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{});
defer pg.terminate() catch {};
defer pg.deinit();
const url = try pg.connectionString(alloc);
defer alloc.free(url);
try testing.expect(url.len > 0);
}const pg = try tc.modules.postgres.run(&provider, tc.modules.postgres.default_image, .{
.username = "user",
.password = "pass",
.database = "testdb",
});
const url = try pg.connectionString(alloc);
// postgres://user:pass@localhost:PORT/testdbconst db = try tc.modules.mysql.run(&provider, tc.modules.mysql.default_image, .{
.username = "user",
.password = "pass",
.database = "testdb",
});
const url = try db.connectionString(alloc);
// user:pass@tcp(localhost:PORT)/testdbconst redis = try tc.modules.redis.run(&provider, tc.modules.redis.default_image, .{});
const url = try redis.connectionString(alloc);
// redis://localhost:PORTconst mongo = try tc.modules.mongodb.run(&provider, tc.modules.mongodb.default_image, .{});
const url = try mongo.connectionString(alloc);
// mongodb://localhost:PORT/const mq = try tc.modules.rabbitmq.run(&provider, tc.modules.rabbitmq.default_image, .{});
const amqp = try mq.amqpURL(alloc); // amqp://guest:guest@localhost:PORT
const http = try mq.httpURL(alloc); // http://localhost:MGMT_PORTconst minio = try tc.modules.minio.run(&provider, tc.modules.minio.default_image, .{});
const url = try minio.connectionString(alloc); // http://localhost:PORTconst es = try tc.modules.elasticsearch.run(&provider, tc.modules.elasticsearch.default_image, .{});
const url = try es.httpURL(alloc); // http://localhost:PORTconst kafka = try tc.modules.kafka.run(&provider, tc.modules.kafka.default_image, .{});
const brokers = try kafka.brokers(alloc); // localhost:PORTconst ls = try tc.modules.localstack.run(&provider, tc.modules.localstack.default_image, .{});
const endpoint = try ls.endpointURL(alloc); // http://localhost:PORTconst ctr = try provider.runContainer(alloc, req);
defer ctr.terminate() catch |err| std.log.err("terminate: {}", .{err});
defer ctr.deinit();
// ctr is automatically stopped and removed when the scope exitsconst ctr = try tc.run(alloc, "postgres:15", .{
.exposed_ports = &.{"5432/tcp"},
.wait_strategy = tc.wait.forAll(&.{
tc.wait.forPort("5432/tcp"),
tc.wait.forLog("database system is ready"),
}),
});const ctr = try tc.run(alloc, "postgres:15", .{
.exposed_ports = &.{"5432/tcp"},
.env = &.{
"POSTGRES_DB=testdb",
"POSTGRES_USER=testuser",
"POSTGRES_PASSWORD=testpass",
},
.wait_strategy = tc.wait.forPort("5432/tcp"),
});const net = try tc.network.Network.create(&client, "app-network", alloc);
defer net.remove(&client, alloc) catch {};
const db = try tc.run(alloc, "postgres:15", .{
.networks = &.{"app-network"},
.network_aliases = &.{.{ .network = "app-network", .alias = "database" }},
.wait_strategy = tc.wait.forPort("5432/tcp"),
});
defer db.terminate() catch {};
defer db.deinit();
// Other containers on "app-network" can reach Postgres at hostname "database".Symptom: Connection refused or error.FileNotFound on socket path.
Solution: Ensure Docker is running:
docker psOverride the socket path via environment variable if needed:
export DOCKER_HOST=/path/to/docker.sockFetch stdout/stderr to diagnose startup failures:
const logs = try ctr.logs(alloc);
defer alloc.free(logs);
std.debug.print("{s}\n", .{logs});Increase the timeout on the container request:
const ctr = try tc.run(alloc, "myimage:latest", .{
.wait_strategy = tc.wait.forPort("8080/tcp"),
.startup_timeout_ns = 120 * std.time.ns_per_s,
});- Read ARCHITECTURE.md for design details
- See examples/basic.zig for a runnable example
- Read CONTRIBUTING.md to contribute
- Open an issue on GitHub
- Ask on Stack Overflow with the
testcontainerstag - Join the Testcontainers Slack