diff --git a/src/Testcontainers/Containers/ExecFailedException.cs b/src/Testcontainers/Containers/ExecFailedException.cs
new file mode 100644
index 000000000..aed316606
--- /dev/null
+++ b/src/Testcontainers/Containers/ExecFailedException.cs
@@ -0,0 +1,62 @@
+namespace DotNet.Testcontainers.Containers
+{
+ using System;
+ using System.Linq;
+ using System.Text;
+ using JetBrains.Annotations;
+
+ ///
+ /// Represents an exception that is thrown when executing a command inside a
+ /// running container fails.
+ ///
+ [PublicAPI]
+ public sealed class ExecFailedException : Exception
+ {
+ private static readonly string[] LineEndings = new[] { "\r\n", "\n" };
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The result of the failed command execution.
+ public ExecFailedException(ExecResult execResult)
+ : base(CreateMessage(execResult))
+ {
+ ExecResult = execResult;
+ }
+
+ ///
+ /// Gets the result of the failed command execution inside the container.
+ ///
+ public ExecResult ExecResult { get; }
+
+ private static string CreateMessage(ExecResult execResult)
+ {
+ var exceptionInfo = new StringBuilder(256);
+ exceptionInfo.Append($"Process exited with code {execResult.ExitCode}.");
+
+ if (!string.IsNullOrEmpty(execResult.Stdout))
+ {
+ var stdoutLines = execResult.Stdout
+ .Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)
+ .Select(line => " " + line);
+
+ exceptionInfo.AppendLine();
+ exceptionInfo.AppendLine(" Stdout: ");
+ exceptionInfo.Append(string.Join(Environment.NewLine, stdoutLines));
+ }
+
+ if (!string.IsNullOrEmpty(execResult.Stderr))
+ {
+ var stderrLines = execResult.Stderr
+ .Split(LineEndings, StringSplitOptions.RemoveEmptyEntries)
+ .Select(line => " " + line);
+
+ exceptionInfo.AppendLine();
+ exceptionInfo.AppendLine(" Stderr: ");
+ exceptionInfo.Append(string.Join(Environment.NewLine, stderrLines));
+ }
+
+ return exceptionInfo.ToString();
+ }
+ }
+}
diff --git a/src/Testcontainers/Containers/ExecResultExtensions.cs b/src/Testcontainers/Containers/ExecResultExtensions.cs
new file mode 100644
index 000000000..f51e04bf2
--- /dev/null
+++ b/src/Testcontainers/Containers/ExecResultExtensions.cs
@@ -0,0 +1,33 @@
+namespace DotNet.Testcontainers.Containers
+{
+ using System;
+ using System.Threading.Tasks;
+
+ ///
+ /// Extension methods for working with instances.
+ ///
+ public static class ExecResultExtensions
+ {
+ ///
+ /// Awaits the and throws an exception if the result's exit code is not successful.
+ ///
+ /// The task returning an .
+ /// A list of exit codes that should be treated as successful. If none are provided, only exit code 0 is treated as successful.
+ /// The if the exit code is in the list of success exit codes.
+ /// Thrown if the exit code is not in the list of success exit codes.
+ public static async Task ThrowOnFailure(this Task execTask, params long[] successExitCodes)
+ {
+ successExitCodes = successExitCodes == null || successExitCodes.Length == 0 ? new long[] { 0 } : successExitCodes;
+
+ var execResult = await execTask
+ .ConfigureAwait(false);
+
+ if (Array.IndexOf(successExitCodes, execResult.ExitCode) < 0)
+ {
+ throw new ExecFailedException(execResult);
+ }
+
+ return execResult;
+ }
+ }
+}
diff --git a/tests/Testcontainers.Platform.Linux.Tests/ExecFailedExceptionTest.cs b/tests/Testcontainers.Platform.Linux.Tests/ExecFailedExceptionTest.cs
new file mode 100644
index 000000000..152038618
--- /dev/null
+++ b/tests/Testcontainers.Platform.Linux.Tests/ExecFailedExceptionTest.cs
@@ -0,0 +1,50 @@
+namespace Testcontainers.Tests;
+
+public sealed class ExecFailedExceptionTest
+{
+ public static readonly List> ExecResultTestData
+ = new List>
+ {
+ new TheoryDataRow
+ (
+ new ExecResult("Stdout\nStdout", "Stderr\nStderr", 1),
+ "Process exited with code 1." + Environment.NewLine +
+ " Stdout: " + Environment.NewLine +
+ " Stdout" + Environment.NewLine +
+ " Stdout" + Environment.NewLine +
+ " Stderr: " + Environment.NewLine +
+ " Stderr" + Environment.NewLine +
+ " Stderr"
+ ),
+ new TheoryDataRow
+ (
+ new ExecResult("Stdout\nStdout", string.Empty, 1),
+ "Process exited with code 1." + Environment.NewLine +
+ " Stdout: " + Environment.NewLine +
+ " Stdout" + Environment.NewLine +
+ " Stdout"
+ ),
+ new TheoryDataRow
+ (
+ new ExecResult(string.Empty, "Stderr\nStderr", 1),
+ "Process exited with code 1." + Environment.NewLine +
+ " Stderr: " + Environment.NewLine +
+ " Stderr" + Environment.NewLine +
+ " Stderr"
+ ),
+ new TheoryDataRow
+ (
+ new ExecResult(string.Empty, string.Empty, 1),
+ "Process exited with code 1."
+ ),
+ };
+
+ [Theory]
+ [MemberData(nameof(ExecResultTestData))]
+ public void ExecFailedExceptionCreatesExpectedMessage(ExecResult execResult, string message)
+ {
+ var exception = new ExecFailedException(execResult);
+ Assert.Equal(execResult, exception.ExecResult);
+ Assert.Equal(message, exception.Message);
+ }
+}
\ No newline at end of file
diff --git a/tests/Testcontainers.Platform.Linux.Tests/ExecResultExtensionsTest.cs b/tests/Testcontainers.Platform.Linux.Tests/ExecResultExtensionsTest.cs
new file mode 100644
index 000000000..c5a1baba3
--- /dev/null
+++ b/tests/Testcontainers.Platform.Linux.Tests/ExecResultExtensionsTest.cs
@@ -0,0 +1,50 @@
+namespace Testcontainers.Tests;
+
+public sealed class ExecResultExtensionsTest : IAsyncLifetime
+{
+ private readonly IContainer _container = new ContainerBuilder()
+ .WithImage(CommonImages.Alpine)
+ .WithCommand(CommonCommands.SleepInfinity)
+ .Build();
+
+ public async ValueTask InitializeAsync()
+ {
+ await _container.StartAsync()
+ .ConfigureAwait(false);
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ return _container.DisposeAsync();
+ }
+
+ [Fact]
+ public async Task ExecAsyncShouldSucceedWhenCommandReturnsZeroExitCode()
+ {
+ // Given
+ var command = new[] { "true" };
+
+ // When
+ var exception = await Record.ExceptionAsync(() => _container.ExecAsync(command, TestContext.Current.CancellationToken).ThrowOnFailure())
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public async Task ExecAsyncShouldThrowExecFailedExceptionWhenCommandFails()
+ {
+ // Given
+ var command = new[] { "/bin/sh", "-c", "echo out; echo err >&2; exit 1" };
+
+ // When
+ var exception = await Assert.ThrowsAsync(() => _container.ExecAsync(command, TestContext.Current.CancellationToken).ThrowOnFailure())
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Equal(1, exception.ExecResult.ExitCode);
+ Assert.Equal("out", exception.ExecResult.Stdout.Trim());
+ Assert.Equal("err", exception.ExecResult.Stderr.Trim());
+ }
+}
\ No newline at end of file