diff --git a/README.md b/README.md
index 89544c6..be453da 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Contains two examples of [bootique-jdbc](https://github.com/bootique/bootique-jd
* [jdbc-app-demo](https://github.com/bootique-examples/bootique-jdbc-demo/tree/master/jdbc-app-demo) - core JDBC API
* [jdbc-test-demo-inmemory-db](https://github.com/bootique-examples/bootique-jdbc-demo/tree/master/jdbc-test-demo-inmemory-db) - JUnit 5 tests using Derby in-memory DB
-* TODO: testcontainers demo
+* [jdbc-test-demo-testcontainer](https://github.com/bootique-examples/bootique-jdbc-demo/tree/master/jdbc-test-demo-testcontainer) - JUnit 5 tests using TestContainers with Docker
For the examples working with the older versions of Bootique check the code on the branches:
diff --git a/jdbc-test-demo-testcontainers/README.md b/jdbc-test-demo-testcontainers/README.md
new file mode 100644
index 0000000..8abf977
--- /dev/null
+++ b/jdbc-test-demo-testcontainers/README.md
@@ -0,0 +1,18 @@
+# jdbc-test-demo-testcontainer
+
+Provides example JUnit5 tests using Bootique JDBC test API.
+
+## Prerequisites
+
+* Java 11 or newer
+* Apache Maven
+* Docker
+
+## Run the demo tests
+
+You need to have running Docker then you can run tests in bootique-jdbc-test demo module using TestContainer with Docker:
+```bash
+git clone git@github.com:bootique-examples/bootique-jdbc-demo.git
+cd jdbc-test-demo-testcontainer
+mvn clean test
+```
\ No newline at end of file
diff --git a/jdbc-test-demo-testcontainers/pom.xml b/jdbc-test-demo-testcontainers/pom.xml
new file mode 100644
index 0000000..85938e9
--- /dev/null
+++ b/jdbc-test-demo-testcontainers/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ io.bootique.jdbc.demo
+ jdbc-test-demo-testcontainers
+ 2.0.RC1
+
+
+ 11
+ UTF-8
+ UTF-8
+ 1.16.0
+ 42.3.1
+ 1.7.25
+
+
+
+
+
+ io.bootique.bom
+ bootique-bom
+ 2.0.RC1
+ import
+ pom
+
+
+
+
+
+
+ io.bootique.jdbc
+ bootique-jdbc-junit5-testcontainers
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+ test
+
+
+ org.testcontainers
+ postgresql
+ ${testcontainers.version}
+ test
+
+
+ org.postgresql
+ postgresql
+ ${postgresql.version}
+ test
+
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+
+
+
\ No newline at end of file
diff --git a/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/BasicTest.java b/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/BasicTest.java
new file mode 100644
index 0000000..b41ee32
--- /dev/null
+++ b/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/BasicTest.java
@@ -0,0 +1,39 @@
+package io.bootique.jdbc.test;
+
+import io.bootique.BQRuntime;
+import io.bootique.Bootique;
+import io.bootique.jdbc.junit5.DbTester;
+import io.bootique.jdbc.junit5.Table;
+import io.bootique.jdbc.junit5.tc.TcDbTester;
+import io.bootique.junit5.BQApp;
+import io.bootique.junit5.BQTest;
+import io.bootique.junit5.BQTestScope;
+import io.bootique.junit5.BQTestTool;
+
+@BQTest
+public abstract class BasicTest {
+
+ // create a test DB
+ @BQTestTool(BQTestScope.GLOBAL)
+ static final DbTester db = TcDbTester
+ .db("jdbc:tc:postgresql:14:///mydb")
+
+ // make sure schema is created
+ .initDB("classpath:testcontainer-schema.sql")
+
+ // make sure test data is deleted before each test
+ .deleteBeforeEachTest("t1");
+
+ // create a global test app object, but do not run any commands
+ @BQApp(value = BQTestScope.GLOBAL, skipRun = true)
+ static final BQRuntime app = Bootique.app()
+ .autoLoadModules()
+
+ // make sure the test app is connected to our test DB
+ .module(db.moduleWithTestDataSource("db"))
+ .createRuntime();
+
+ protected Table t1() {
+ return db.getTable("t1");
+ }
+}
\ No newline at end of file
diff --git a/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/InsertSelectUpdateTest.java b/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/InsertSelectUpdateTest.java
new file mode 100644
index 0000000..20cd9ed
--- /dev/null
+++ b/jdbc-test-demo-testcontainers/src/test/java/io.bootique.jdbc.test/InsertSelectUpdateTest.java
@@ -0,0 +1,36 @@
+package io.bootique.jdbc.test;
+
+import org.junit.jupiter.api.Test;
+
+import java.sql.Types;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class InsertSelectUpdateTest extends BasicTest{
+
+ @Test
+ public void testUpdate() {
+
+ // implicit column names and positions
+ t1().insert(1, "x", "y");
+
+ // explicit column names
+ t1().insertColumns("c1", "c2", "c3")
+ .values(2, "a", "b")
+ .exec();
+
+ t1().update()
+ .set("c3", "c", Types.VARCHAR)
+ .where("c1", 2)
+ .exec();
+
+ // Note that "matcher" API is preferred for assertions, but sometimes you do need to select the data in a test
+ List