diff --git a/helpers/Dojo/ExerciceDockerCompose.ts b/helpers/Dojo/ExerciceDockerCompose.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bb873671189f394d2b9cbce62be61c97773b4d24
--- /dev/null
+++ b/helpers/Dojo/ExerciceDockerCompose.ts
@@ -0,0 +1,151 @@
+import EnonceFile            from '../../../shared/types/Dojo/EnonceFile';
+import { TypedEmitter }      from 'tiny-typed-emitter';
+import ExerciceRunningEvents from '../../types/Dojo/ExerciceRunningEvents';
+import { spawn }             from 'child_process';
+import ExerciceCheckerError  from '../../../shared/types/Dojo/ExerciceCheckerError';
+
+
+class ExerciceDockerCompose {
+    readonly events: TypedEmitter<ExerciceRunningEvents> = new TypedEmitter<ExerciceRunningEvents>();
+
+    public displayableLogs: string = '';
+    public allLogs: string = '';
+
+    public isFinished: boolean = false;
+    public success: boolean = false;
+    public exitCode: number = -1;
+
+    constructor(private projectName: string, private enonceFile: EnonceFile, private executionFolder: string, private composeFileOverride: Array<string> = []) {
+        this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
+            this.allLogs += log;
+            this.displayableLogs += displayable ? log : '';
+        });
+
+        this.events.on('finished', (success: boolean, exitCode: number) => {
+            this.isFinished = true;
+            this.success = success;
+            this.exitCode = exitCode;
+        });
+    }
+
+    run(doDown: boolean = false) {
+        (async () => {
+            let containerExitCode: number = -1;
+
+            const dockerComposeCommand = `docker compose --project-name ${ this.projectName } --progress plain --file docker-compose.yml ${ this.composeFileOverride.map((file) => `--file ${ file }`).join(' ') }`;
+
+            // Run the service
+            {
+                try {
+                    this.events.emit('step', 'COMPOSE_RUN', 'Run Docker Compose file');
+
+                    containerExitCode = await new Promise<number>((resolve, reject) => {
+
+                        this.events.emit('logs', '####################################################### Docker Compose & Main Container Logs #######################################################\n', false, false);
+
+                        const dockerCompose = spawn(`${ dockerComposeCommand } run --build --rm ${ this.enonceFile.result.container }`, {
+                            cwd  : this.executionFolder,
+                            shell: true,
+                            env  : {
+                                'DOCKER_BUILDKIT'  : '1',
+                                'BUILDKIT_PROGRESS': 'plain', ...process.env
+                            }
+                        });
+
+                        dockerCompose.stdout.on('data', (data) => {
+                            this.events.emit('logs', data.toString(), false, true);
+                        });
+
+                        dockerCompose.stderr.on('data', (data) => {
+                            this.events.emit('logs', data.toString(), true, true);
+                        });
+
+                        dockerCompose.on('exit', (code) => {
+                            code !== null ? resolve(code) : reject();
+                        });
+                    });
+                } catch ( error ) {
+                    this.events.emit('endStep', 'COMPOSE_RUN', `Error while running the docker compose file`, true);
+                    this.events.emit('finished', false, ExerciceCheckerError.DOCKER_COMPOSE_RUN_ERROR);
+                    return;
+                }
+                this.events.emit('endStep', 'COMPOSE_RUN', `Docker Compose file run successfully`, false);
+            }
+
+            // Get linked services logs
+            {
+                try {
+                    this.events.emit('step', 'COMPOSE_LOGS', 'Logs acquisition of linked services');
+
+                    await new Promise<void>((resolve, reject) => {
+
+                        this.events.emit('logs', '####################################################### Other Services Logs #######################################################\n', false, false);
+
+                        const dockerCompose = spawn(`${ dockerComposeCommand } logs --timestamps`, {
+                            cwd  : this.executionFolder,
+                            shell: true
+                        });
+
+                        dockerCompose.stdout.on('data', (data) => {
+                            this.events.emit('logs', data.toString(), false, false);
+                        });
+
+                        dockerCompose.stderr.on('data', (data) => {
+                            this.events.emit('logs', data.toString(), true, false);
+                        });
+
+                        dockerCompose.on('exit', (code) => {
+                            code !== null ? resolve() : reject();
+                        });
+                    });
+                } catch ( error ) {
+                    this.events.emit('endStep', 'COMPOSE_LOGS', `Error while getting the linked services logs`, true);
+                    this.events.emit('finished', false, ExerciceCheckerError.DOCKER_COMPOSE_LOGS_ERROR);
+                    return;
+                }
+                this.events.emit('endStep', 'COMPOSE_LOGS', `Linked services logs acquired`, false);
+            }
+
+            // Remove containers if asked
+            {
+                if ( doDown ) {
+                    try {
+                        this.events.emit('step', 'COMPOSE_DOWN', 'Stopping and removing containers');
+
+                        await new Promise<void>((resolve, reject) => {
+
+                            this.events.emit('logs', '####################################################### Stop and remove containers #######################################################\n', false, false);
+
+                            const dockerCompose = spawn(`${ dockerComposeCommand } down --volumes`, {
+                                cwd  : this.executionFolder,
+                                shell: true
+                            });
+
+                            dockerCompose.stdout.on('data', (data) => {
+                                this.events.emit('logs', data.toString(), false, false);
+                            });
+
+                            dockerCompose.stderr.on('data', (data) => {
+                                this.events.emit('logs', data.toString(), true, false);
+                            });
+
+                            dockerCompose.on('exit', (code) => {
+                                code !== null ? resolve() : reject();
+                            });
+                        });
+                    } catch ( error ) {
+                        this.events.emit('endStep', 'COMPOSE_DOWN', `Error stop and remove containers`, true);
+                        this.events.emit('finished', false, ExerciceCheckerError.DOCKER_COMPOSE_DOWN_ERROR);
+                        return;
+                    }
+                    this.events.emit('endStep', 'COMPOSE_DOWN', `Containers stopped and removed`, false);
+                }
+            }
+
+            this.events.emit('finished', true, containerExitCode);
+        })();
+    }
+}
+
+
+export default ExerciceDockerCompose;
\ No newline at end of file