diff --git a/src/Aspire.Hosting.Docker/DockerComposeFileResource.cs b/src/Aspire.Hosting.Docker/DockerComposeFileResource.cs
new file mode 100644
index 00000000000..863767745c2
--- /dev/null
+++ b/src/Aspire.Hosting.Docker/DockerComposeFileResource.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Hosting.ApplicationModel;
+
+namespace Aspire.Hosting.Docker;
+
+/// 
+/// Represents a Docker Compose file resource that imports services from a docker-compose.yml file.
+/// 
+/// The name of the resource.
+/// The path to the docker-compose.yml file.
+public class DockerComposeFileResource(string name, string composeFilePath) : Resource(name)
+{
+    /// 
+    /// Gets the path to the docker-compose.yml file.
+    /// 
+    public string ComposeFilePath { get; } = composeFilePath;
+
+    /// 
+    /// Gets the mapping of service names to their container resource builders.
+    /// 
+    internal Dictionary> ServiceBuilders { get; } = new(StringComparer.OrdinalIgnoreCase);
+}
diff --git a/src/Aspire.Hosting.Docker/DockerComposeFileResourceBuilderExtensions.cs b/src/Aspire.Hosting.Docker/DockerComposeFileResourceBuilderExtensions.cs
new file mode 100644
index 00000000000..fdbe3f26241
--- /dev/null
+++ b/src/Aspire.Hosting.Docker/DockerComposeFileResourceBuilderExtensions.cs
@@ -0,0 +1,480 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Hosting.ApplicationModel;
+using Aspire.Hosting.Docker;
+using Aspire.Hosting.Utils;
+using Microsoft.Extensions.Logging;
+
+namespace Aspire.Hosting;
+
+/// 
+/// Provides extension methods for adding Docker Compose file resources to the application model.
+/// 
+public static class DockerComposeFileResourceBuilderExtensions
+{
+    /// 
+    /// Adds a Docker Compose file to the application model by parsing the compose file and creating container resources.
+    /// 
+    /// The .
+    /// The name of the resource.
+    /// The path to the docker-compose.yml file.
+    /// A reference to the .
+    /// 
+    /// This method parses the docker-compose.yml file and translates supported services into Aspire container resources.
+    /// Services that cannot be translated are skipped with a warning.
+    /// All created resources are children of the DockerComposeFileResource.
+    /// 
+    /// 
+    /// 
+    /// var builder = DistributedApplication.CreateBuilder(args);
+    ///
+    /// builder.AddDockerComposeFile("mycompose", "./docker-compose.yml");
+    ///
+    /// builder.Build().Run();
+    /// 
+    /// 
+    public static IResourceBuilder AddDockerComposeFile(
+        this IDistributedApplicationBuilder builder,
+        [ResourceName] string name,
+        string composeFilePath)
+    {
+        ArgumentNullException.ThrowIfNull(builder);
+        ArgumentException.ThrowIfNullOrEmpty(name);
+        ArgumentException.ThrowIfNullOrEmpty(composeFilePath);
+
+        // Resolve the compose file path to a full physical path relative to the app host directory
+        var fullComposeFilePath = Path.GetFullPath(composeFilePath, builder.AppHostDirectory);
+
+        var resource = new DockerComposeFileResource(name, fullComposeFilePath);
+        
+        // Parse and import the compose file synchronously to add resources to the model
+        // Capture any exceptions to report during initialization
+        Exception? parseException = null;
+        List warnings = [];
+        
+        try
+        {
+            ParseAndImportComposeFile(builder, resource, fullComposeFilePath, warnings);
+        }
+        catch (Exception ex)
+        {
+            parseException = ex;
+        }
+        
+        // Use OnInitializeResource to report any issues that occurred during parsing
+        return builder.AddResource(resource).ExcludeFromManifest().OnInitializeResource(async (resource, e, ct) =>
+        {
+            if (parseException is not null)
+            {
+                e.Logger.LogError(parseException, "Failed to parse Docker Compose file: {ComposeFilePath}", composeFilePath);
+                await e.Notifications.PublishUpdateAsync(resource, s => s with { State = KnownResourceStates.FailedToStart }).ConfigureAwait(false);
+                return;
+            }
+
+            foreach (var warning in warnings)
+            {
+                e.Logger.LogWarning("{Warning}", warning);
+            }
+
+            await e.Notifications.PublishUpdateAsync(resource, s => s with { State = KnownResourceStates.Running }).ConfigureAwait(false);
+        });
+    }
+
+    private static void ParseAndImportComposeFile(
+        IDistributedApplicationBuilder builder,
+        DockerComposeFileResource parentResource,
+        string composeFilePath,
+        List warnings)
+    {
+        if (!File.Exists(composeFilePath))
+        {
+            throw new FileNotFoundException($"Docker Compose file not found: {composeFilePath}", composeFilePath);
+        }
+
+        var yamlContent = File.ReadAllText(composeFilePath);
+        
+        Dictionary services;
+        try
+        {
+            services = DockerComposeParser.ParseComposeFile(yamlContent);
+        }
+        catch (Exception ex)
+        {
+            throw new InvalidOperationException($"Failed to parse Docker Compose file: {composeFilePath}", ex);
+        }
+
+        if (services.Count == 0)
+        {
+            warnings.Add($"No services found in Docker Compose file: {composeFilePath}");
+            return;
+        }
+
+        // Collect all unique placeholders across all services and create parameters for them
+        var uniquePlaceholders = new Dictionary(StringComparer.Ordinal);
+        foreach (var service in services.Values)
+        {
+            foreach (var (placeholderName, placeholder) in service.Placeholders)
+            {
+                if (!uniquePlaceholders.ContainsKey(placeholderName))
+                {
+                    uniquePlaceholders[placeholderName] = placeholder;
+                }
+            }
+        }
+
+        // Create ParameterResource for each unique placeholder
+        var parameters = new Dictionary(StringComparer.Ordinal);
+        foreach (var (placeholderName, placeholder) in uniquePlaceholders)
+        {
+            IResourceBuilder paramBuilder;
+            
+            if (placeholder.DefaultValue != null)
+            {
+                // Create parameter with default value
+                paramBuilder = builder.AddParameter(placeholderName, placeholder.DefaultValue);
+            }
+            else
+            {
+                // Create parameter without default (will need to be provided via configuration or user input)
+                paramBuilder = builder.AddParameter(placeholderName);
+            }
+            
+            parameters[placeholderName] = paramBuilder.Resource;
+        }
+
+        // First pass: Create all container resources
+        foreach (var (serviceName, service) in services)
+        {
+            try
+            {
+                var containerBuilder = ImportService(builder, parentResource, serviceName, service, composeFilePath, parameters, warnings);
+                if (containerBuilder is not null)
+                {
+                    parentResource.ServiceBuilders[serviceName] = containerBuilder;
+                }
+            }
+            catch (Exception ex)
+            {
+                warnings.Add($"Failed to import service '{serviceName}' from Docker Compose file: {ex.Message}");
+            }
+        }
+
+        // Second pass: Set up dependencies (depends_on)
+        foreach (var (serviceName, service) in services)
+        {
+            if (service.DependsOn.Count == 0)
+            {
+                continue;
+            }
+
+            if (!parentResource.ServiceBuilders.TryGetValue(serviceName, out var containerBuilder))
+            {
+                continue; // Service was skipped
+            }
+
+            foreach (var (dependencyName, dependency) in service.DependsOn)
+            {
+                if (!parentResource.ServiceBuilders.TryGetValue(dependencyName, out var dependencyBuilder))
+                {
+                    warnings.Add($"Service '{serviceName}' depends on '{dependencyName}', but '{dependencyName}' was not imported.");
+                    continue;
+                }
+
+                try
+                {
+                    // Map Docker Compose condition to Aspire WaitFor methods
+                    var condition = dependency.Condition?.ToLowerInvariant();
+                    switch (condition)
+                    {
+                        case "service_started":
+                            containerBuilder.WaitForStart(dependencyBuilder);
+                            break;
+                        case "service_healthy":
+                            containerBuilder.WaitFor(dependencyBuilder);
+                            break;
+                        case "service_completed_successfully":
+                            containerBuilder.WaitForCompletion(dependencyBuilder);
+                            break;
+                        case null:
+                        case "":
+                            // Default behavior: wait for service to start
+                            containerBuilder.WaitForStart(dependencyBuilder);
+                            break;
+                        default:
+                            warnings.Add($"Unknown depends_on condition '{dependency.Condition}' for service '{serviceName}' -> '{dependencyName}'. Using default (service_started).");
+                            containerBuilder.WaitForStart(dependencyBuilder);
+                            break;
+                    }
+                }
+                catch (Exception ex)
+                {
+                    warnings.Add($"Failed to set up dependency for service '{serviceName}' -> '{dependencyName}': {ex.Message}");
+                }
+            }
+        }
+    }
+
+    private static IResourceBuilder? ImportService(IDistributedApplicationBuilder builder, DockerComposeFileResource parentResource, string serviceName, ParsedService service, string composeFilePath, Dictionary parameters, List warnings)
+    {
+        IResourceBuilder containerBuilder;
+
+        // Check if service has a build configuration
+        if (service.Build is not null)
+        {
+            // Use AddDockerfile for services with build configurations
+            // Resolve context path relative to the compose file's directory
+            var contextPath = service.Build.Context ?? ".";
+            var composeFileDirectory = Path.GetDirectoryName(composeFilePath)!;
+            var resolvedContextPath = Path.GetFullPath(contextPath, composeFileDirectory);
+            
+            var dockerfilePath = service.Build.Dockerfile;
+            var stage = service.Build.Target;
+
+            containerBuilder = builder.AddDockerfile(serviceName, resolvedContextPath, dockerfilePath, stage)
+                .WithAnnotation(new ResourceRelationshipAnnotation(parentResource, "parent"));
+            
+            // Add build args if present
+            if (service.Build.Args.Count > 0)
+            {
+                foreach (var (key, value) in service.Build.Args)
+                {
+                    containerBuilder.WithBuildArg(key, value);
+                }
+            }
+        }
+        else if (!string.IsNullOrWhiteSpace(service.Image))
+        {
+            // Use AddContainer for services with pre-built images
+            // Parse image using ContainerReferenceParser
+            ContainerReference containerRef;
+            try
+            {
+                containerRef = ContainerReferenceParser.Parse(service.Image);
+            }
+            catch (Exception ex)
+            {
+                warnings.Add($"Failed to parse image reference '{service.Image}' for service '{serviceName}': {ex.Message}");
+                return null;
+            }
+
+            var imageName = containerRef.Registry is null 
+                ? containerRef.Image 
+                : $"{containerRef.Registry}/{containerRef.Image}";
+            var imageTag = containerRef.Tag ?? "latest";
+
+            containerBuilder = builder.AddContainer(serviceName, imageName, imageTag)
+                .WithAnnotation(new ResourceRelationshipAnnotation(parentResource, "parent"));
+        }
+        else
+        {
+            // Skip services without an image or build configuration
+            warnings.Add($"Service '{serviceName}' has neither image nor build configuration. Skipping.");
+            return null;
+        }
+
+        // Import environment variables
+        if (service.Environment.Count > 0)
+        {
+            foreach (var (key, envVar) in service.Environment)
+            {
+                if (envVar.IsLiteral)
+                {
+                    // Simple literal value
+                    containerBuilder.WithEnvironment(key, envVar.LiteralValue!);
+                }
+                else
+                {
+                    // Value contains placeholders - convert to ReferenceExpression using parameters
+                    var expression = CreateReferenceExpressionFromPlaceholders(envVar, parameters);
+                    containerBuilder.WithEnvironment(key, expression);
+                }
+            }
+        }
+
+        // Import ports
+        if (service.Ports.Count > 0)
+        {
+            foreach (var port in service.Ports)
+            {
+                if (port.Target.HasValue)
+                {
+                    // Determine scheme based on protocol
+                    // Short syntax with explicit /tcp → use tcp scheme (for raw TCP connections)
+                    // Long syntax with protocol:tcp → convert to http (common web scenario)
+                    // No protocol specified → default to http
+                    // UDP → use udp scheme
+                    var scheme = port.Protocol?.ToLowerInvariant() switch
+                    {
+                        "udp" => "udp",
+                        "tcp" when port.IsShortSyntax => "tcp", // Short syntax /tcp means raw TCP
+                        "tcp" => "http", // Long syntax tcp means HTTP over TCP
+                        null => "http",
+                        _ => "http"
+                    };
+                    
+                    // Use the port name from long syntax if available, otherwise generate one
+                    var endpointName = !string.IsNullOrWhiteSpace(port.Name) 
+                        ? port.Name 
+                        : (port.Published.HasValue ? $"port{port.Published.Value}" : $"port{port.Target.Value}");
+                    
+                    containerBuilder.WithEndpoint(
+                        name: endpointName,
+                        scheme: scheme,
+                        port: port.Published,
+                        targetPort: port.Target.Value,
+                        isExternal: true,
+                        isProxied: false);
+                }
+            }
+        }
+
+        // Import volumes
+        if (service.Volumes.Count > 0)
+        {
+            foreach (var volume in service.Volumes)
+            {
+                if (!string.IsNullOrWhiteSpace(volume.Target))
+                {
+                    if (string.IsNullOrWhiteSpace(volume.Source))
+                    {
+                        // Anonymous volume - just target path
+                        containerBuilder.WithVolume(volume.Target);
+                    }
+                    else
+                    {
+                        var isReadOnly = volume.ReadOnly;
+                        if (volume.Type == "bind")
+                        {
+                            containerBuilder.WithBindMount(volume.Source, volume.Target, isReadOnly);
+                        }
+                        else
+                        {
+                            containerBuilder.WithVolume(volume.Source, volume.Target, isReadOnly);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Import command
+        if (service.Command.Count > 0)
+        {
+            containerBuilder.WithArgs(service.Command.ToArray());
+        }
+
+        // Import entrypoint  
+        if (service.Entrypoint.Count > 0)
+        {
+            // WithEntrypoint expects a single string, so join them with space
+            containerBuilder.WithEntrypoint(string.Join(" ", service.Entrypoint));
+        }
+
+        return containerBuilder;
+    }
+
+    /// 
+    /// Gets a container resource builder for a specific service defined in the Docker Compose file.
+    /// 
+    /// The .
+    /// The name of the service as defined in the docker-compose.yml file.
+    /// The  for the specified service.
+    /// Thrown when the service is not found in the compose file.
+    /// 
+    /// 
+    /// var builder = DistributedApplication.CreateBuilder(args);
+    ///
+    /// var compose = builder.AddDockerComposeFile("mycompose", "./docker-compose.yml");
+    /// 
+    /// // Get a reference to a specific service to configure it further
+    /// var webService = compose.GetComposeService("web");
+    /// webService.WithEnvironment("ADDITIONAL_VAR", "value");
+    ///
+    /// builder.Build().Run();
+    /// 
+    /// 
+    public static IResourceBuilder GetComposeService(
+        this IResourceBuilder builder,
+        string serviceName)
+    {
+        ArgumentNullException.ThrowIfNull(builder);
+        ArgumentException.ThrowIfNullOrEmpty(serviceName);
+
+        if (!builder.Resource.ServiceBuilders.TryGetValue(serviceName, out var serviceBuilder))
+        {
+            throw new InvalidOperationException($"Service '{serviceName}' not found in Docker Compose file '{builder.Resource.ComposeFilePath}'. Available services: {string.Join(", ", builder.Resource.ServiceBuilders.Keys)}");
+        }
+
+        return serviceBuilder;
+    }
+
+    /// 
+    /// Creates a ReferenceExpression from a ParsedEnvironmentVariable containing placeholders.
+    /// 
+    private static ReferenceExpression CreateReferenceExpressionFromPlaceholders(ParsedEnvironmentVariable envVar, Dictionary parameters)
+    {
+        var builder = new ReferenceExpressionBuilder();
+        
+        // Parse the format string and append literal parts and placeholder parts
+        var parts = envVar.Format!.Split(new[] { '{', '}' });
+        var isPlaceholder = false;
+        
+        for (int i = 0; i < parts.Length; i++)
+        {
+            var part = parts[i];
+            
+            if (string.IsNullOrEmpty(part))
+            {
+                isPlaceholder = !isPlaceholder;
+                continue;
+            }
+            
+            if (isPlaceholder && int.TryParse(part, out var index))
+            {
+                // This is a placeholder reference - append the parameter resource
+                var placeholder = envVar.Placeholders[index];
+                if (parameters.TryGetValue(placeholder.Name, out var parameterResource))
+                {
+                    builder.AppendFormatted(parameterResource);
+                }
+                else
+                {
+                    // Fallback to default value if parameter not found (shouldn't happen)
+                    builder.AppendLiteral(placeholder.DefaultValue ?? string.Empty);
+                }
+            }
+            else
+            {
+                // Literal text
+                builder.AppendLiteral(part);
+            }
+            
+            isPlaceholder = !isPlaceholder;
+        }
+        
+        return builder.Build();
+    }
+}
+
+/// 
+/// A value provider that returns a parameter's default value.
+/// This is kept for backwards compatibility but is no longer used in the current implementation.
+/// 
+file class ParameterDefault : IValueProvider, IManifestExpressionProvider
+{
+    private readonly string _value;
+    private readonly string _name;
+
+    public ParameterDefault(string name, string value)
+    {
+        _name = name;
+        _value = value;
+    }
+
+    public string ValueExpression => $"${{{_name}}}";
+
+    public ValueTask GetValueAsync(CancellationToken cancellationToken = default)
+    {
+        return new ValueTask(_value);
+    }
+}
diff --git a/src/Aspire.Hosting.Docker/DockerComposeParser.cs b/src/Aspire.Hosting.Docker/DockerComposeParser.cs
new file mode 100644
index 00000000000..c6cea507c4e
--- /dev/null
+++ b/src/Aspire.Hosting.Docker/DockerComposeParser.cs
@@ -0,0 +1,900 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using YamlDotNet.RepresentationModel;
+
+namespace Aspire.Hosting.Docker;
+
+/// 
+/// Parses Docker Compose service definitions and normalizes various format variations
+/// according to the Docker Compose specification.
+/// 
+internal static class DockerComposeParser
+{
+    /// 
+    /// Parses a Docker Compose YAML file using low-level YamlDotNet APIs.
+    /// 
+    /// The YAML content to parse.
+    /// A dictionary of service names to parsed service definitions.
+    public static Dictionary ParseComposeFile(string yaml)
+    {
+        var services = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+        using var reader = new StringReader(yaml);
+        var yamlStream = new YamlStream();
+        yamlStream.Load(reader);
+
+        if (yamlStream.Documents.Count == 0)
+        {
+            return services;
+        }
+
+        var rootNode = yamlStream.Documents[0].RootNode as YamlMappingNode;
+        if (rootNode == null)
+        {
+            return services;
+        }
+
+        // Find the "services" node
+        var servicesKey = new YamlScalarNode("services");
+        if (!rootNode.Children.TryGetValue(servicesKey, out var servicesNode) || servicesNode is not YamlMappingNode servicesMapping)
+        {
+            return services;
+        }
+
+        // Parse each service
+        foreach (var serviceEntry in servicesMapping.Children)
+        {
+            if (serviceEntry.Key is not YamlScalarNode serviceNameNode)
+            {
+                continue;
+            }
+
+            var serviceName = serviceNameNode.Value ?? string.Empty;
+            if (string.IsNullOrEmpty(serviceName))
+            {
+                continue;
+            }
+
+            if (serviceEntry.Value is not YamlMappingNode serviceNode)
+            {
+                continue;
+            }
+
+            var parsedService = ParseService(serviceNode);
+            services[serviceName] = parsedService;
+        }
+
+        return services;
+    }
+
+    private static ParsedService ParseService(YamlMappingNode serviceNode)
+    {
+        var service = new ParsedService();
+
+        foreach (var property in serviceNode.Children)
+        {
+            if (property.Key is not YamlScalarNode keyNode)
+            {
+                continue;
+            }
+
+            var key = keyNode.Value;
+            switch (key)
+            {
+                case "image":
+                    if (property.Value is YamlScalarNode imageNode)
+                    {
+                        service.Image = imageNode.Value;
+                    }
+                    break;
+
+                case "build":
+                    service.Build = ParseBuild(property.Value);
+                    break;
+
+                case "environment":
+                    service.Environment = ParseEnvironmentFromYaml(property.Value);
+                    break;
+
+                case "ports":
+                    service.Ports = ParsePortsFromYaml(property.Value);
+                    break;
+
+                case "volumes":
+                    service.Volumes = ParseVolumesFromYaml(property.Value);
+                    break;
+
+                case "command":
+                    service.Command = ParseCommandOrEntrypoint(property.Value);
+                    break;
+
+                case "entrypoint":
+                    service.Entrypoint = ParseCommandOrEntrypoint(property.Value);
+                    break;
+
+                case "depends_on":
+                    service.DependsOn = ParseDependsOnFromYaml(property.Value);
+                    break;
+            }
+        }
+
+        // Collect unique placeholders from environment variables
+        foreach (var envVar in service.Environment.Values)
+        {
+            if (!envVar.IsLiteral)
+            {
+                foreach (var placeholder in envVar.Placeholders)
+                {
+                    // Add to service placeholders dictionary if not already present
+                    if (!service.Placeholders.ContainsKey(placeholder.Name))
+                    {
+                        service.Placeholders[placeholder.Name] = placeholder;
+                    }
+                }
+            }
+        }
+
+        return service;
+    }
+
+    private static ParsedBuild? ParseBuild(YamlNode node)
+    {
+        if (node is YamlScalarNode scalarNode)
+        {
+            // Short syntax: build: ./dir
+            return new ParsedBuild { Context = scalarNode.Value };
+        }
+
+        if (node is not YamlMappingNode mappingNode)
+        {
+            return null;
+        }
+
+        var build = new ParsedBuild();
+
+        foreach (var property in mappingNode.Children)
+        {
+            if (property.Key is not YamlScalarNode keyNode)
+            {
+                continue;
+            }
+
+            var key = keyNode.Value;
+            switch (key)
+            {
+                case "context":
+                    if (property.Value is YamlScalarNode contextNode)
+                    {
+                        build.Context = contextNode.Value;
+                    }
+                    break;
+
+                case "dockerfile":
+                    if (property.Value is YamlScalarNode dockerfileNode)
+                    {
+                        build.Dockerfile = dockerfileNode.Value;
+                    }
+                    break;
+
+                case "target":
+                    if (property.Value is YamlScalarNode targetNode)
+                    {
+                        build.Target = targetNode.Value;
+                    }
+                    break;
+
+                case "args":
+                    build.Args = ParseBuildArgs(property.Value);
+                    break;
+            }
+        }
+
+        return build;
+    }
+
+    private static Dictionary ParseBuildArgs(YamlNode node)
+    {
+        var args = new Dictionary(StringComparer.Ordinal);
+
+        if (node is YamlMappingNode mappingNode)
+        {
+            foreach (var arg in mappingNode.Children)
+            {
+                if (arg.Key is YamlScalarNode keyNode && arg.Value is YamlScalarNode valueNode)
+                {
+                    args[keyNode.Value ?? string.Empty] = valueNode.Value ?? string.Empty;
+                }
+            }
+        }
+        else if (node is YamlSequenceNode sequenceNode)
+        {
+            // Array format: args: ["KEY=value"]
+            foreach (var item in sequenceNode.Children)
+            {
+                if (item is YamlScalarNode scalarNode && scalarNode.Value != null)
+                {
+                    var parts = scalarNode.Value.Split('=', 2);
+                    if (parts.Length == 2)
+                    {
+                        args[parts[0]] = parts[1];
+                    }
+                    else if (parts.Length == 1)
+                    {
+                        args[parts[0]] = string.Empty;
+                    }
+                }
+            }
+        }
+
+        return args;
+    }
+
+    private static Dictionary ParseEnvironmentFromYaml(YamlNode node)
+    {
+        var result = new Dictionary(StringComparer.Ordinal);
+
+        if (node is YamlMappingNode mappingNode)
+        {
+            // Dictionary format: {KEY: value, KEY2: value2}
+            foreach (var env in mappingNode.Children)
+            {
+                if (env.Key is YamlScalarNode keyNode && env.Value is YamlScalarNode valueNode)
+                {
+                    var key = keyNode.Value ?? string.Empty;
+                    var value = valueNode.Value ?? string.Empty;
+                    
+                    result[key] = ParseEnvironmentValue(value);
+                }
+            }
+        }
+        else if (node is YamlSequenceNode sequenceNode)
+        {
+            // Array format: ["KEY=value", "KEY2=value2"]
+            foreach (var item in sequenceNode.Children)
+            {
+                if (item is YamlScalarNode scalarNode && scalarNode.Value != null)
+                {
+                    var parts = scalarNode.Value.Split('=', 2);
+                    if (parts.Length == 2)
+                    {
+                        result[parts[0]] = ParseEnvironmentValue(parts[1]);
+                    }
+                    else if (parts.Length == 1)
+                    {
+                        // Variable without value (e.g., "DEBUG") - include it as empty literal
+                        result[parts[0]] = new ParsedEnvironmentVariable { LiteralValue = string.Empty };
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /// 
+    /// Parses an environment variable value that may contain Docker Compose placeholders.
+    /// 
+    /// The raw environment variable value.
+    /// A ParsedEnvironmentVariable containing either a literal value or placeholder information.
+    private static ParsedEnvironmentVariable ParseEnvironmentValue(string value)
+    {
+        if (string.IsNullOrEmpty(value))
+        {
+            return new ParsedEnvironmentVariable { LiteralValue = value };
+        }
+
+        var placeholders = new List();
+        var formatParts = new List();
+        var currentPart = new System.Text.StringBuilder();
+        var index = 0;
+        var placeholderIndex = 0;
+
+        while (index < value.Length)
+        {
+            if (value[index] == '$' && index + 1 < value.Length)
+            {
+                // Check for escaped placeholder ($$)
+                if (value[index + 1] == '$')
+                {
+                    // Escaped - treat as literal $
+                    currentPart.Append('$');
+                    index += 2;
+                    continue;
+                }
+
+                // Check for placeholder (${...})
+                if (value[index + 1] == '{')
+                {
+                    var closeBrace = value.IndexOf('}', index + 2);
+                    if (closeBrace == -1)
+                    {
+                        // Malformed placeholder - treat as literal
+                        currentPart.Append(value[index]);
+                        index++;
+                        continue;
+                    }
+
+                    // Extract placeholder content
+                    var placeholderContent = value.Substring(index + 2, closeBrace - index - 2);
+                    
+                    // Treat empty placeholders as literal ${}
+                    if (string.IsNullOrWhiteSpace(placeholderContent))
+                    {
+                        currentPart.Append("${}");
+                        index = closeBrace + 1;
+                        continue;
+                    }
+                    
+                    var placeholder = ParsePlaceholder(placeholderContent);
+
+                    // Add current part to format and start new placeholder
+                    formatParts.Add(currentPart.ToString());
+                    formatParts.Add($"{{{placeholderIndex}}}");
+                    placeholders.Add(placeholder);
+                    currentPart.Clear();
+                    placeholderIndex++;
+
+                    index = closeBrace + 1;
+                    continue;
+                }
+            }
+
+            currentPart.Append(value[index]);
+            index++;
+        }
+
+        // If no placeholders were found, return as literal
+        if (placeholders.Count == 0)
+        {
+            return new ParsedEnvironmentVariable { LiteralValue = currentPart.ToString() };
+        }
+
+        // Add final part
+        formatParts.Add(currentPart.ToString());
+
+        // Build format string by combining parts
+        var format = string.Join("", formatParts);
+
+        return new ParsedEnvironmentVariable
+        {
+            Format = format,
+            Placeholders = placeholders
+        };
+    }
+
+    /// 
+    /// Parses a Docker Compose placeholder content (the part between ${ and }).
+    /// Supports: VAR, VAR:-default, VAR-default, VAR:?error, VAR?error
+    /// 
+    private static ParsedPlaceholder ParsePlaceholder(string content)
+    {
+        // Check for :- syntax (use default if unset or empty)
+        var colonMinusIndex = content.IndexOf(":-");
+        if (colonMinusIndex > 0)
+        {
+            return new ParsedPlaceholder
+            {
+                Name = content.Substring(0, colonMinusIndex),
+                DefaultValue = content.Substring(colonMinusIndex + 2),
+                DefaultType = PlaceholderDefaultType.ColonMinus
+            };
+        }
+
+        // Check for - syntax (use default if unset)
+        var minusIndex = content.IndexOf('-');
+        if (minusIndex > 0 && (minusIndex == content.Length - 1 || content[minusIndex - 1] != ':'))
+        {
+            return new ParsedPlaceholder
+            {
+                Name = content.Substring(0, minusIndex),
+                DefaultValue = content.Substring(minusIndex + 1),
+                DefaultType = PlaceholderDefaultType.Minus
+            };
+        }
+
+        // Check for :? or ? syntax (required with error) - we ignore the error message
+        var colonQuestionIndex = content.IndexOf(":?");
+        if (colonQuestionIndex > 0)
+        {
+            return new ParsedPlaceholder
+            {
+                Name = content.Substring(0, colonQuestionIndex),
+                DefaultValue = null,
+                DefaultType = PlaceholderDefaultType.None
+            };
+        }
+
+        var questionIndex = content.IndexOf('?');
+        if (questionIndex > 0)
+        {
+            return new ParsedPlaceholder
+            {
+                Name = content.Substring(0, questionIndex),
+                DefaultValue = null,
+                DefaultType = PlaceholderDefaultType.None
+            };
+        }
+
+        // Simple placeholder ${VAR}
+        return new ParsedPlaceholder
+        {
+            Name = content,
+            DefaultValue = null,
+            DefaultType = PlaceholderDefaultType.None
+        };
+    }
+
+    private static List ParsePortsFromYaml(YamlNode node)
+    {
+        var result = new List();
+
+        if (node is not YamlSequenceNode sequenceNode)
+        {
+            return result;
+        }
+
+        foreach (var item in sequenceNode.Children)
+        {
+            if (item is YamlScalarNode scalarNode && scalarNode.Value != null)
+            {
+                // Short syntax: "8080:80" or "8080:80/tcp" or "127.0.0.1:8080:80"
+                var port = ParseShortPortSyntax(scalarNode.Value);
+                result.Add(new ParsedPort
+                {
+                    Target = port.Target,
+                    Published = port.Published,
+                    Protocol = port.Protocol,
+                    HostIp = port.HostIp,
+                    Name = port.Name,
+                    IsShortSyntax = true
+                });
+            }
+            else if (item is YamlMappingNode mappingNode)
+            {
+                // Long syntax: {target: 80, published: 8080, protocol: tcp, name: web}
+                int? target = null;
+                int? published = null;
+                string? protocol = null;
+                string? hostIp = null;
+                string? name = null;
+
+                foreach (var prop in mappingNode.Children)
+                {
+                    if (prop.Key is not YamlScalarNode keyNode || prop.Value is not YamlScalarNode valueNode)
+                    {
+                        continue;
+                    }
+
+                    switch (keyNode.Value)
+                    {
+                        case "target":
+                            if (int.TryParse(valueNode.Value, out var t))
+                            {
+                                target = t;
+                            }
+                            break;
+                        case "published":
+                            if (int.TryParse(valueNode.Value, out var p))
+                            {
+                                published = p;
+                            }
+                            break;
+                        case "protocol":
+                            protocol = valueNode.Value;
+                            break;
+                        case "host_ip":
+                            hostIp = valueNode.Value;
+                            break;
+                        case "name":
+                            name = valueNode.Value;
+                            break;
+                    }
+                }
+
+                result.Add(new ParsedPort
+                {
+                    Target = target,
+                    Published = published,
+                    Protocol = protocol, // Keep null if not specified
+                    HostIp = hostIp,
+                    Name = name,
+                    IsShortSyntax = false // Long syntax
+                });
+            }
+        }
+
+        return result;
+    }
+
+    private static ParsedPort ParseShortPortSyntax(string portSpec)
+    {
+        string? protocol = null;
+        var spec = portSpec;
+
+        // Extract protocol if present (e.g., "8080:80/udp")
+        if (spec.Contains('/'))
+        {
+            var parts = spec.Split('/');
+            spec = parts[0];
+            protocol = parts[1].ToLowerInvariant();
+        }
+
+        string? hostIp = null;
+        int? published = null;
+        int? target = null;
+
+        var portParts = spec.Split(':');
+
+        if (portParts.Length == 1)
+        {
+            // Just target port: "3000"
+            if (int.TryParse(portParts[0], out var t))
+            {
+                target = t;
+            }
+        }
+        else if (portParts.Length == 2)
+        {
+            // Published:target: "8080:80"
+            if (int.TryParse(portParts[0], out var p) && int.TryParse(portParts[1], out var t))
+            {
+                published = p;
+                target = t;
+            }
+        }
+        else if (portParts.Length == 3)
+        {
+            // HostIP:Published:target: "127.0.0.1:8080:80"
+            hostIp = portParts[0];
+            if (int.TryParse(portParts[1], out var p) && int.TryParse(portParts[2], out var t))
+            {
+                published = p;
+                target = t;
+            }
+        }
+
+        return new ParsedPort
+        {
+            Target = target,
+            Published = published,
+            Protocol = protocol, // Keep null if not specified
+            HostIp = hostIp
+        };
+    }
+
+    private static List ParseVolumesFromYaml(YamlNode node)
+    {
+        var result = new List();
+
+        if (node is not YamlSequenceNode sequenceNode)
+        {
+            return result;
+        }
+
+        foreach (var item in sequenceNode.Children)
+        {
+            if (item is YamlScalarNode scalarNode && scalarNode.Value != null)
+            {
+                // Short syntax: "./source:/target:ro"
+                result.Add(ParseShortVolumeSyntax(scalarNode.Value));
+            }
+            else if (item is YamlMappingNode mappingNode)
+            {
+                // Long syntax: {type: bind, source: ./src, target: /app, read_only: true}
+                string? type = null;
+                string? source = null;
+                string? target = null;
+                bool readOnly = false;
+
+                foreach (var prop in mappingNode.Children)
+                {
+                    if (prop.Key is not YamlScalarNode keyNode || prop.Value is not YamlScalarNode valueNode)
+                    {
+                        continue;
+                    }
+
+                    switch (keyNode.Value)
+                    {
+                        case "type":
+                            type = valueNode.Value;
+                            break;
+                        case "source":
+                            source = valueNode.Value;
+                            break;
+                        case "target":
+                            target = valueNode.Value;
+                            break;
+                        case "read_only":
+                            readOnly = valueNode.Value?.ToLowerInvariant() == "true";
+                            break;
+                    }
+                }
+
+                if (target != null)
+                {
+                    result.Add(new VolumeMount
+                    {
+                        Type = type ?? "volume",
+                        Source = source,
+                        Target = target,
+                        ReadOnly = readOnly
+                    });
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private static List ParseCommandOrEntrypoint(YamlNode node)
+    {
+        var result = new List();
+
+        if (node is YamlScalarNode scalarNode && scalarNode.Value != null)
+        {
+            // Single string: command: "echo hello"
+            result.Add(scalarNode.Value);
+        }
+        else if (node is YamlSequenceNode sequenceNode)
+        {
+            // Array: command: ["echo", "hello"]
+            foreach (var item in sequenceNode.Children)
+            {
+                if (item is YamlScalarNode itemNode && itemNode.Value != null)
+                {
+                    result.Add(itemNode.Value);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private static Dictionary ParseDependsOnFromYaml(YamlNode node)
+    {
+        var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+        if (node is YamlSequenceNode sequenceNode)
+        {
+            // Simple format: ["service1", "service2"]
+            foreach (var item in sequenceNode.Children)
+            {
+                if (item is YamlScalarNode scalarNode && scalarNode.Value != null)
+                {
+                    result[scalarNode.Value] = new ParsedDependency { Condition = "service_started" };
+                }
+            }
+        }
+        else if (node is YamlMappingNode mappingNode)
+        {
+            // Long format: {service1: {condition: service_healthy}}
+            foreach (var dep in mappingNode.Children)
+            {
+                if (dep.Key is not YamlScalarNode keyNode || keyNode.Value == null)
+                {
+                    continue;
+                }
+
+                var serviceName = keyNode.Value;
+                var condition = "service_started"; // Default
+
+                if (dep.Value is YamlMappingNode depConfig)
+                {
+                    var conditionKey = new YamlScalarNode("condition");
+                    if (depConfig.Children.TryGetValue(conditionKey, out var conditionNode) && 
+                        conditionNode is YamlScalarNode conditionScalar)
+                    {
+                        condition = conditionScalar.Value ?? "service_started";
+                    }
+                }
+
+                result[serviceName] = new ParsedDependency { Condition = condition };
+            }
+        }
+
+        return result;
+    }
+
+    private static VolumeMount ParseShortVolumeSyntax(string volumeSpec)
+    {
+        var parts = volumeSpec.Split(':');
+        string? source = null;
+        string target;
+        bool readOnly = false;
+        string type = "volume";
+
+        if (parts.Length == 1)
+        {
+            // Anonymous volume: "/target"
+            target = parts[0];
+            type = "volume";
+        }
+        else if (parts.Length >= 2)
+        {
+            source = parts[0];
+            target = parts[1];
+
+            // Determine type based on source
+            if (source.StartsWith("./") || source.StartsWith("../") || source.StartsWith("/") || (source.Length > 1 && source[1] == ':'))
+            {
+                type = "bind";
+            }
+
+            if (parts.Length >= 3)
+            {
+                readOnly = parts[2].Contains("ro");
+            }
+        }
+        else
+        {
+            throw new InvalidOperationException($"Invalid volume specification: {volumeSpec}");
+        }
+
+        return new VolumeMount
+        {
+            Type = type,
+            Source = source,
+            Target = target,
+            ReadOnly = readOnly
+        };
+    }
+}
+
+/// 
+/// Represents a parsed Docker Compose service.
+/// 
+internal class ParsedService
+{
+    public string? Image { get; set; }
+    public ParsedBuild? Build { get; set; }
+    public Dictionary Environment { get; set; } = new(StringComparer.Ordinal);
+    public List Ports { get; set; } = [];
+    public List Volumes { get; set; } = [];
+    public List Command { get; set; } = [];
+    public List Entrypoint { get; set; } = [];
+    public Dictionary DependsOn { get; set; } = new(StringComparer.OrdinalIgnoreCase);
+    public Dictionary Placeholders { get; set; } = new(StringComparer.Ordinal);
+}
+
+/// 
+/// Represents a parsed build configuration.
+/// 
+internal class ParsedBuild
+{
+    public string? Context { get; set; }
+    public string? Dockerfile { get; set; }
+    public string? Target { get; set; }
+    public Dictionary Args { get; set; } = new(StringComparer.Ordinal);
+}
+
+/// 
+/// Represents a parsed service dependency.
+/// 
+internal class ParsedDependency
+{
+    public string Condition { get; set; } = "service_started";
+}
+
+/// 
+/// Represents a parsed port mapping from Docker Compose.
+/// Spec: https://github.com/compose-spec/compose-spec/blob/master/spec.md#ports
+/// 
+internal class ParsedPort
+{
+    /// 
+    /// The container port (required).
+    /// 
+    public int? Target { get; init; }
+    
+    /// 
+    /// The host port (optional - if not specified, a random port is assigned).
+    /// 
+    public int? Published { get; init; }
+    
+    /// 
+    /// The protocol (tcp or udp). Null if not explicitly specified in the compose file.
+    /// 
+    public string? Protocol { get; init; }
+    
+    /// 
+    /// The host IP to bind to (optional).
+    /// 
+    public string? HostIp { get; init; }
+    
+    /// 
+    /// Optional human-readable name for the port (from long syntax 'name' field).
+    /// 
+    public string? Name { get; init; }
+    
+    /// 
+    /// Indicates if this port was defined using short syntax (true) or long syntax (false).
+    /// This affects how tcp protocol is interpreted for scheme determination.
+    /// 
+    public bool IsShortSyntax { get; init; }
+}
+
+/// 
+/// Represents a parsed volume mount.
+/// 
+internal record VolumeMount
+{
+    public string Type { get; init; } = "volume";
+    public string? Source { get; init; }
+    public required string Target { get; init; }
+    public bool ReadOnly { get; init; }
+}
+
+/// 
+/// Represents a Docker Compose environment variable placeholder.
+/// Spec: https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation
+/// 
+internal class ParsedPlaceholder
+{
+    /// 
+    /// The name of the placeholder variable (e.g., "DATABASE_URL").
+    /// 
+    public required string Name { get; init; }
+    
+    /// 
+    /// The default value if specified (e.g., "localhost" in ${DB_HOST:-localhost}).
+    /// Null if no default was specified.
+    /// 
+    public string? DefaultValue { get; init; }
+    
+    /// 
+    /// The type of default syntax used.
+    /// - ColonMinus (:-) means use default if variable is unset or empty
+    /// - Minus (-) means use default only if variable is unset
+    /// - None means no default specified
+    /// 
+    public PlaceholderDefaultType DefaultType { get; init; }
+}
+
+/// 
+/// The type of default value syntax used in a placeholder.
+/// 
+internal enum PlaceholderDefaultType
+{
+    /// 
+    /// No default value specified (e.g., ${VAR}).
+    /// 
+    None,
+    
+    /// 
+    /// Use default if variable is unset or empty (e.g., ${VAR:-default}).
+    /// 
+    ColonMinus,
+    
+    /// 
+    /// Use default only if variable is unset (e.g., ${VAR-default}).
+    /// 
+    Minus
+}
+
+/// 
+/// Represents a parsed environment variable value that can be either a literal string
+/// or a formatted string with placeholders.
+/// 
+internal class ParsedEnvironmentVariable
+{
+    /// 
+    /// The literal string value if the environment variable contains no placeholders.
+    /// 
+    public string? LiteralValue { get; init; }
+    
+    /// 
+    /// The format string if the environment variable contains placeholders (e.g., "postgres://{0}:{1}/{2}").
+    /// 
+    public string? Format { get; init; }
+    
+    /// 
+    /// The ordered list of placeholders referenced in the format string.
+    /// 
+    public List Placeholders { get; init; } = [];
+    
+    /// 
+    /// True if this is a literal value, false if it contains placeholders.
+    /// 
+    public bool IsLiteral => LiteralValue != null;
+}
diff --git a/src/Aspire.Hosting.Docker/README.md b/src/Aspire.Hosting.Docker/README.md
index f48b67ccd12..3ce5afc1bb8 100644
--- a/src/Aspire.Hosting.Docker/README.md
+++ b/src/Aspire.Hosting.Docker/README.md
@@ -12,18 +12,48 @@ In your AppHost project, install the Aspire Docker Hosting library with [NuGet](
 dotnet add package Aspire.Hosting.Docker
 ```
 
-## Usage example
+## Usage examples
 
-Then, in the _AppHost.cs_ file of `AppHost`, add the environment:
+### Publishing to Docker Compose
+
+To publish an Aspire application to Docker Compose, add the Docker Compose environment in the _AppHost.cs_ file:
 
 ```csharp
 builder.AddDockerComposeEnvironment("compose");
 ```
 
+Then publish using the Aspire CLI:
+
 ```shell
 aspire publish -o docker-compose-artifacts
 ```
 
+### Importing from Docker Compose
+
+You can import existing Docker Compose files into your Aspire application model using `AddDockerComposeFile`:
+
+```csharp
+var builder = DistributedApplication.CreateBuilder(args);
+
+// Import services from a docker-compose.yml file
+builder.AddDockerComposeFile("myservices", "./docker-compose.yml");
+
+builder.Build().Run();
+```
+
+This will parse the Docker Compose file and create Aspire container resources for each service that has an `image` specified. The following Docker Compose features are supported:
+
+- **Image**: Container image name and tag
+- **Ports**: Port mappings (mapped to Aspire endpoints)
+- **Environment**: Environment variables
+- **Volumes**: Both bind mounts and named volumes
+- **Command**: Container command arguments
+- **Entrypoint**: Container entrypoint
+- **Build**: Services with build configurations are imported using `AddDockerfile`
+- **Depends On**: Service dependencies are mapped to `WaitFor`, `WaitForStart`, or `WaitForCompletion` based on the condition
+
+**Note**: Other Docker Compose features like networks, health checks, and restart policies are not automatically imported but can be configured manually on the created resources.
+
 ## Feedback & contributing
 
 https://github.com/dotnet/aspire
diff --git a/src/Aspire.Hosting/Utils/ContainerReferenceParser.cs b/src/Aspire.Hosting/Utils/ContainerReferenceParser.cs
index 1cf2083615a..5e14c4b9d30 100644
--- a/src/Aspire.Hosting/Utils/ContainerReferenceParser.cs
+++ b/src/Aspire.Hosting/Utils/ContainerReferenceParser.cs
@@ -7,8 +7,14 @@ namespace Aspire.Hosting.Utils;
 /// 
 /// Class to parse container references (e.g. "mcr.microsoft.com/dotnet/sdk:8.0")
 /// 
-internal sealed partial class ContainerReferenceParser
+public sealed partial class ContainerReferenceParser
 {
+    /// 
+    /// Parses a container reference string into its components.
+    /// 
+    /// The container reference string to parse (e.g., "mcr.microsoft.com/dotnet/sdk:8.0").
+    /// A  containing the parsed components.
+    /// Thrown when the input is invalid or cannot be parsed.
     public static ContainerReference Parse(string input)
     {
         if (string.IsNullOrEmpty(input))
@@ -40,6 +46,13 @@ public static ContainerReference Parse(string input)
     private static partial Regex ImageNameRegex();
 }
 
-internal record struct ContainerReference(string? Registry, string Image, string? Tag, string? Digest)
+/// 
+/// Represents a parsed container reference with its registry, image name, tag, and digest components.
+/// 
+/// The registry hostname (e.g., "mcr.microsoft.com"), or null if not specified.
+/// The image name (e.g., "dotnet/sdk").
+/// The image tag (e.g., "8.0"), or null if not specified.
+/// The image digest, or null if not specified.
+public record struct ContainerReference(string? Registry, string Image, string? Tag, string? Digest)
 {
 }
diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposeFileTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposeFileTests.cs
new file mode 100644
index 00000000000..aadc176c339
--- /dev/null
+++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposeFileTests.cs
@@ -0,0 +1,813 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Hosting.ApplicationModel;
+using Aspire.Hosting.Utils;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Aspire.Hosting.Docker.Tests;
+
+public class DockerComposeFileTests(ITestOutputHelper output)
+{
+    [Fact]
+    public void AddDockerComposeFile_ParsesSimpleService()
+    {
+        // Create a temp docker-compose.yml file
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  redis:
+    image: redis:7.0
+    ports:
+      - ""6379:6379""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            // Add the docker compose file
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            // Verify the compose resource was created
+            Assert.NotNull(composeResource);
+            Assert.Equal("mycompose", composeResource.Resource.Name);
+            Assert.Equal(composeFilePath, composeResource.Resource.ComposeFilePath);
+            
+            // Build the app and execute initialization hooks
+            var app = builder.Build();
+            
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify that a redis container was created
+            var redisResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "redis");
+            Assert.NotNull(redisResource);
+            
+            // Verify the image was set correctly
+            var imageAnnotation = redisResource.Annotations.OfType().FirstOrDefault();
+            Assert.NotNull(imageAnnotation);
+            Assert.Equal("redis", imageAnnotation.Image);
+            Assert.Equal("7.0", imageAnnotation.Tag);
+            
+            // Verify the endpoint was created
+            var endpoints = redisResource.Annotations.OfType();
+            Assert.NotEmpty(endpoints);
+            var endpoint = endpoints.First();
+            Assert.Equal("port6379", endpoint.Name);
+            Assert.Equal(6379, endpoint.Port);
+            Assert.Equal(6379, endpoint.TargetPort);
+        }
+        finally
+        {
+            tempDir.Delete(recursive: true);
+        }
+    }
+    
+    [Fact]
+    public void AddDockerComposeFile_ParsesMultipleServices()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  web:
+    image: nginx:latest
+    ports:
+      - ""8080:80""
+  db:
+    image: postgres:14
+    environment:
+      POSTGRES_PASSWORD: secret
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify web service
+            var webResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "web");
+            Assert.NotNull(webResource);
+            
+            // Verify db service
+            var dbResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "db");
+            Assert.NotNull(dbResource);
+            
+            // Verify environment variable was set on db
+            var envAnnotations = dbResource.Annotations.OfType();
+            Assert.NotEmpty(envAnnotations);
+        }
+        finally
+        {
+            tempDir.Delete(recursive: true);
+        }
+    }
+    
+    [Fact]
+    public void AddDockerComposeFile_CapturesFileNotFoundError()
+    {
+        using var builder = TestDistributedApplicationBuilder.Create();
+        
+        // Should capture FileNotFoundException but not throw immediately
+        // Exception will be logged during initialization
+        var composeResource = builder.AddDockerComposeFile("mycompose", "/nonexistent/docker-compose.yml");
+        Assert.NotNull(composeResource);
+        Assert.Equal("mycompose", composeResource.Resource.Name);
+        
+        // Build should succeed - the error is logged during initialization, not thrown
+        var app = builder.Build();
+        var appModel = app.Services.GetRequiredService();
+        
+        // Verify the compose resource exists but no services were imported
+        var composeFileResource = appModel.Resources.OfType().FirstOrDefault();
+        Assert.NotNull(composeFileResource);
+        
+        // No container services should be imported
+        var containers = appModel.Resources.OfType();
+        Assert.Empty(containers);
+    }
+    
+    [Fact]
+    public void AddDockerComposeFile_ImportsServicesWithBuildConfiguration()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  app:
+    build:
+      context: .
+    ports:
+      - ""3000:3000""
+  cache:
+    image: redis:latest
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify that both services were created
+            var cacheResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "cache");
+            Assert.NotNull(cacheResource);
+            
+            // app service should now be imported via AddDockerfile since it has build configuration
+            var appResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "app");
+            Assert.NotNull(appResource);
+            
+            // Verify app has build annotation
+            var buildAnnotation = appResource.Annotations.OfType().FirstOrDefault();
+            Assert.NotNull(buildAnnotation);
+        }
+        finally
+        {
+            tempDir.Delete(recursive: true);
+        }
+    }
+    
+    [Fact]
+    public void AddDockerComposeFile_ParsesVolumeMounts()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  app:
+    image: myapp:latest
+    volumes:
+      - type: bind
+        source: ./data
+        target: /app/data
+        read_only: true
+      - type: volume
+        source: appdata
+        target: /var/lib/app
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            
+            var appModel = app.Services.GetRequiredService();
+            
+            var appResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "app");
+            Assert.NotNull(appResource);
+            
+            var mounts = appResource.Annotations.OfType();
+            Assert.NotEmpty(mounts);
+            Assert.Equal(2, mounts.Count());
+        }
+        finally
+        {
+            tempDir.Delete(recursive: true);
+        }
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_ComprehensiveExample()
+    {
+        // Use the test-docker-compose.yml file from the test directory
+        var composeFilePath = Path.Combine(Directory.GetCurrentDirectory(), "test-docker-compose.yml");
+        
+        if (!File.Exists(composeFilePath))
+        {
+            output.WriteLine($"Warning: test-docker-compose.yml not found at {composeFilePath}, skipping test");
+            return;
+        }
+
+        using var builder = TestDistributedApplicationBuilder.Create();
+        
+        builder.AddDockerComposeFile("testcompose", composeFilePath);
+        
+        var app = builder.Build();
+        
+        var appModel = app.Services.GetRequiredService();
+        
+        // Verify all three services were created
+        var webResource = appModel.Resources.OfType()
+            .FirstOrDefault(r => r.Name == "web");
+        Assert.NotNull(webResource);
+        
+        var redisResource = appModel.Resources.OfType()
+            .FirstOrDefault(r => r.Name == "redis");
+        Assert.NotNull(redisResource);
+        
+        var postgresResource = appModel.Resources.OfType()
+            .FirstOrDefault(r => r.Name == "postgres");
+        Assert.NotNull(postgresResource);
+        
+        // Verify web service has correct image
+        var webImage = webResource.Annotations.OfType().FirstOrDefault();
+        Assert.NotNull(webImage);
+        Assert.Equal("nginx", webImage.Image);
+        Assert.Equal("alpine", webImage.Tag);
+        
+        // Verify web service has environment variables
+        var webEnv = webResource.Annotations.OfType();
+        Assert.NotEmpty(webEnv);
+        
+        // Verify web service has endpoints
+        var webEndpoints = webResource.Annotations.OfType();
+        Assert.NotEmpty(webEndpoints);
+        
+        // Verify postgres has environment variables
+        var postgresEnv = postgresResource.Annotations.OfType();
+        Assert.NotEmpty(postgresEnv);
+        
+        // Verify postgres has volumes
+        var postgresVolumes = postgresResource.Annotations.OfType();
+        Assert.NotEmpty(postgresVolumes);
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_SupportsServicesWithBuildConfiguration()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  webapp:
+    build:
+      context: ./app
+      dockerfile: Dockerfile
+      target: production
+      args:
+        NODE_ENV: production
+        API_URL: https://api.example.com
+    ports:
+      - ""3000:3000""
+    environment:
+      PORT: ""3000""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify webapp service was imported with build configuration
+            var webappResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "webapp");
+            Assert.NotNull(webappResource);
+            
+            // Verify it has a Dockerfile build annotation
+            var buildAnnotation = webappResource.Annotations.OfType().FirstOrDefault();
+            Assert.NotNull(buildAnnotation);
+            // Context path will be made absolute by AddDockerfile, so just check it ends with "app"
+            Assert.EndsWith("app", buildAnnotation.ContextPath.Replace('\\', '/'));
+            // Dockerfile path is also made absolute
+            Assert.EndsWith("Dockerfile", buildAnnotation.DockerfilePath?.Replace('\\', '/'));
+            Assert.Equal("production", buildAnnotation.Stage);
+            
+            // Verify build args are added using WithBuildArg
+            Assert.NotEmpty(buildAnnotation.BuildArguments);
+            Assert.True(buildAnnotation.BuildArguments.ContainsKey("NODE_ENV"));
+            Assert.True(buildAnnotation.BuildArguments.ContainsKey("API_URL"));
+            
+            // Verify the endpoint was created with proper name
+            var endpoints = webappResource.Annotations.OfType();
+            Assert.NotEmpty(endpoints);
+            var endpoint = endpoints.First();
+            Assert.Equal("port3000", endpoint.Name);
+            Assert.Equal(3000, endpoint.Port);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_HandlesTcpProtocol()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  tcpservice:
+    image: myapp:latest
+    ports:
+      - ""5000:5000/tcp""
+      - ""8080:8080""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify tcpservice was imported
+            var tcpResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "tcpservice");
+            Assert.NotNull(tcpResource);
+            
+            // Verify endpoints were created
+            var endpoints = tcpResource.Annotations.OfType().ToList();
+            Assert.Equal(2, endpoints.Count);
+            
+            // Verify TCP endpoint
+            var tcpEndpoint = endpoints.FirstOrDefault(e => e.Port == 5000);
+            Assert.NotNull(tcpEndpoint);
+            Assert.Equal("port5000", tcpEndpoint.Name);
+            Assert.Equal("tcp", tcpEndpoint.UriScheme);
+            Assert.Equal(5000, tcpEndpoint.TargetPort);
+            
+            // Verify HTTP endpoint (default when no protocol specified)
+            var httpEndpoint = endpoints.FirstOrDefault(e => e.Port == 8080);
+            Assert.NotNull(httpEndpoint);
+            Assert.Equal("port8080", httpEndpoint.Name);
+            Assert.Equal("http", httpEndpoint.UriScheme);
+            Assert.Equal(8080, httpEndpoint.TargetPort);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_HandlesDependsOn()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  database:
+    image: postgres:15
+    ports:
+      - ""5432:5432""
+  
+  cache:
+    image: redis:7.0
+    ports:
+      - ""6379:6379""
+  
+  app:
+    image: myapp:latest
+    depends_on:
+      database:
+        condition: service_started
+      cache:
+        condition: service_healthy
+    ports:
+      - ""8080:80""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            var app = builder.Build();
+            
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify all services were created
+            var databaseResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "database");
+            Assert.NotNull(databaseResource);
+            
+            var cacheResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "cache");
+            Assert.NotNull(cacheResource);
+            
+            var appResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "app");
+            Assert.NotNull(appResource);
+            
+            // Verify app has WaitAnnotations for dependencies
+            var waitAnnotations = appResource.Annotations.OfType().ToList();
+            Assert.NotEmpty(waitAnnotations);
+            Assert.Equal(2, waitAnnotations.Count);
+            
+            // Verify database dependency (service_started -> WaitUntilStarted)
+            var dbWait = waitAnnotations.FirstOrDefault(w => w.Resource.Name == "database");
+            Assert.NotNull(dbWait);
+            Assert.Equal(WaitType.WaitUntilStarted, dbWait.WaitType);
+            
+            // Verify cache dependency (service_healthy -> WaitUntilHealthy)
+            var cacheWait = waitAnnotations.FirstOrDefault(w => w.Resource.Name == "cache");
+            Assert.NotNull(cacheWait);
+            Assert.Equal(WaitType.WaitUntilHealthy, cacheWait.WaitType);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void GetComposeService_ReturnsServiceBuilder()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+    ports:
+      - ""8080:80""
+  api:
+    image: node:18-alpine
+    ports:
+      - ""3000:3000""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            // Get specific services using GetComposeService
+            var webService = composeResource.GetComposeService("web");
+            var apiService = composeResource.GetComposeService("api");
+            
+            // Verify we got the correct services
+            Assert.NotNull(webService);
+            Assert.Equal("web", webService.Resource.Name);
+            Assert.NotNull(apiService);
+            Assert.Equal("api", apiService.Resource.Name);
+            
+            // Further configure the services
+            webService.WithEnvironment("NGINX_HOST", "example.com");
+            apiService.WithEnvironment("NODE_ENV", "production");
+            
+            var app = builder.Build();
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify the additional environment variables were added
+            var webResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "web");
+            Assert.NotNull(webResource);
+            var webEnvVars = webResource.Annotations.OfType();
+            Assert.NotEmpty(webEnvVars);
+            
+            var apiResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "api");
+            Assert.NotNull(apiResource);
+            var apiEnvVars = apiResource.Annotations.OfType();
+            Assert.NotEmpty(apiEnvVars);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void GetComposeService_ThrowsWhenServiceNotFound()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            // Try to get a service that doesn't exist
+            var exception = Assert.Throws(() => 
+                composeResource.GetComposeService("nonexistent"));
+            
+            Assert.Contains("Service 'nonexistent' not found", exception.Message);
+            Assert.Contains("Available services: web", exception.Message);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void ParsesArrayFormatEnvironmentVariables()
+    {
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+    environment:
+      - NGINX_HOST=localhost
+      - NGINX_PORT=80
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            var webService = composeResource.GetComposeService("web");
+            
+            Assert.NotNull(webService);
+            var containerResource = webService.Resource as ContainerResource;
+            Assert.NotNull(containerResource);
+            
+            // Check that environment variables were parsed from array format
+            Assert.True(containerResource.TryGetAnnotationsIncludingAncestorsOfType(out var envAnnotations));
+            Assert.NotEmpty(envAnnotations);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_ParsesComplexRealWorldExample()
+    {
+        // Test the exact format reported by the user
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+
+services:
+  frontend:
+    build:
+      context: ./frontend
+      dockerfile: Dockerfile
+    ports:
+      - ""3000:3000""
+    environment:
+      - VITE_API_URL=http://localhost:8000
+    volumes:
+      - ./frontend:/app
+      - /app/node_modules
+    depends_on:
+      - backend
+
+  backend:
+    build:
+      context: ./backend
+      dockerfile: Dockerfile
+    ports:
+      - ""8000:8000""
+    environment:
+      - DATABASE_URL=postgresql://user:password@db:5432/appdb
+    volumes:
+      - ./backend:/app
+    depends_on:
+      - db
+
+  db:
+    image: postgres:15
+    environment:
+      - POSTGRES_USER=user
+      - POSTGRES_PASSWORD=password
+      - POSTGRES_DB=appdb
+    ports:
+      - ""5432:5432""
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+
+  redis:
+    image: redis:7-alpine
+    ports:
+      - ""6379:6379""
+    volumes:
+      - redis_data:/data
+
+volumes:
+  postgres_data:
+  redis_data:
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            // Add the docker compose file
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            // Build the app
+            var app = builder.Build();
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify all services were imported
+            var containerResources = appModel.Resources.OfType().ToList();
+            Assert.Equal(4, containerResources.Count); // frontend, backend, db, redis
+            
+            // Verify frontend service
+            var frontend = containerResources.FirstOrDefault(r => r.Name == "frontend");
+            Assert.NotNull(frontend);
+            // Should be a Dockerfile resource since it has build config
+            var frontendEndpoints = frontend.Annotations.OfType().ToList();
+            Assert.Single(frontendEndpoints);
+            Assert.Equal(3000, frontendEndpoints[0].Port);
+            
+            // Verify backend service
+            var backend = containerResources.FirstOrDefault(r => r.Name == "backend");
+            Assert.NotNull(backend);
+            var backendEndpoints = backend.Annotations.OfType().ToList();
+            Assert.Single(backendEndpoints);
+            Assert.Equal(8000, backendEndpoints[0].Port);
+            
+            // Verify db service
+            var db = containerResources.FirstOrDefault(r => r.Name == "db");
+            Assert.NotNull(db);
+            var dbEndpoints = db.Annotations.OfType().ToList();
+            Assert.Single(dbEndpoints);
+            Assert.Equal(5432, dbEndpoints[0].Port);
+            
+            // Verify redis service  
+            var redis = containerResources.FirstOrDefault(r => r.Name == "redis");
+            Assert.NotNull(redis);
+            var redisEndpoints = redis.Annotations.OfType().ToList();
+            Assert.Single(redisEndpoints);
+            Assert.Equal(6379, redisEndpoints[0].Port);
+            
+            // Verify volumes were parsed (check on frontend which has 2 volumes)
+            var frontendMounts = frontend.Annotations.OfType().ToList();
+            Assert.Equal(2, frontendMounts.Count);
+            
+            // Verify environment variables were parsed (check db which has 3 env vars in array format)
+            Assert.True(db.TryGetAnnotationsIncludingAncestorsOfType(out var dbEnvAnnotations));
+            Assert.NotEmpty(dbEnvAnnotations);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+    [Fact]
+    public void AddDockerComposeFile_ParsesLongSyntaxPorts()
+    {
+        // Create a temp docker-compose.yml file with long syntax ports
+        var tempDir = Directory.CreateTempSubdirectory(".docker-compose-file-test");
+        output.WriteLine($"Temp directory: {tempDir.FullName}");
+        
+        var composeFilePath = Path.Combine(tempDir.FullName, "docker-compose.yml");
+        File.WriteAllText(composeFilePath, @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+    ports:
+      - target: 80
+        published: 8080
+        protocol: tcp
+      - target: 443
+        published: 8443
+        protocol: tcp
+        host_ip: 127.0.0.1
+      - ""9000:9000""
+");
+
+        try
+        {
+            using var builder = TestDistributedApplicationBuilder.Create();
+            
+            // Add the docker compose file
+            var composeResource = builder.AddDockerComposeFile("mycompose", composeFilePath);
+            
+            // Build the app to trigger initialization
+            var app = builder.Build();
+            var appModel = app.Services.GetRequiredService();
+            
+            // Verify that web container was created
+            var webResource = appModel.Resources.OfType()
+                .FirstOrDefault(r => r.Name == "web");
+            Assert.NotNull(webResource);
+            
+            // Verify endpoints were created (should have 3 ports)
+            var endpoints = webResource.Annotations.OfType().ToList();
+            Assert.Equal(3, endpoints.Count);
+            
+            // Verify first port (long syntax: 8080:80/tcp)
+            var endpoint1 = endpoints.FirstOrDefault(e => e.Name == "port8080");
+            Assert.NotNull(endpoint1);
+            Assert.Equal(80, endpoint1.TargetPort);
+            Assert.Equal(8080, endpoint1.Port);
+            Assert.Equal("http", endpoint1.UriScheme); // tcp defaults to http
+            
+            // Verify second port (long syntax with host_ip: 127.0.0.1:8443:443/tcp)
+            var endpoint2 = endpoints.FirstOrDefault(e => e.Name == "port8443");
+            Assert.NotNull(endpoint2);
+            Assert.Equal(443, endpoint2.TargetPort);
+            Assert.Equal(8443, endpoint2.Port);
+            
+            // Verify third port (short syntax: 9000:9000)
+            var endpoint3 = endpoints.FirstOrDefault(e => e.Name == "port9000");
+            Assert.NotNull(endpoint3);
+            Assert.Equal(9000, endpoint3.TargetPort);
+            Assert.Equal(9000, endpoint3.Port);
+        }
+        finally
+        {
+            try { tempDir.Delete(recursive: true); } catch { }
+        }
+    }
+
+}
diff --git a/tests/Aspire.Hosting.Docker.Tests/DockerComposeParserTests.cs b/tests/Aspire.Hosting.Docker.Tests/DockerComposeParserTests.cs
new file mode 100644
index 00000000000..e951bc1bf40
--- /dev/null
+++ b/tests/Aspire.Hosting.Docker.Tests/DockerComposeParserTests.cs
@@ -0,0 +1,1060 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Aspire.Hosting.Docker.Tests;
+
+/// 
+/// Tests for DockerComposeParser that verify parsing of various Docker Compose specification formats.
+/// Based on the Compose Specification: https://github.com/compose-spec/compose-spec/blob/master/spec.md
+/// 
+public class DockerComposeParserTests
+{
+    [Fact]
+    public void ParseComposeFile_EmptyYaml_ReturnsEmptyDictionary()
+    {
+        var yaml = "";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        Assert.Empty(result);
+    }
+
+    [Fact]
+    public void ParseComposeFile_NoServices_ReturnsEmptyDictionary()
+    {
+        var yaml = @"
+version: '3.8'
+networks:
+  mynetwork:
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        Assert.Empty(result);
+    }
+
+    [Fact]
+    public void ParseComposeFile_SingleServiceWithImage_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result);
+        Assert.True(result.ContainsKey("web"));
+        Assert.Equal("nginx:alpine", result["web"].Image);
+    }
+
+    #region Environment Variables Tests
+    // Spec: Environment variables can be defined using a dictionary or an array
+
+    [Fact]
+    public void ParseEnvironment_DictionaryFormat_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      DEBUG: 'true'
+      LOG_LEVEL: info
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["app"].Environment.Count);
+        Assert.True(result["app"].Environment["DEBUG"].IsLiteral);
+        Assert.Equal("true", result["app"].Environment["DEBUG"].LiteralValue);
+        Assert.True(result["app"].Environment["LOG_LEVEL"].IsLiteral);
+        Assert.Equal("info", result["app"].Environment["LOG_LEVEL"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParseEnvironment_ArrayFormat_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      - DEBUG=true
+      - LOG_LEVEL=info
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["app"].Environment.Count);
+        Assert.True(result["app"].Environment["DEBUG"].IsLiteral);
+        Assert.Equal("true", result["app"].Environment["DEBUG"].LiteralValue);
+        Assert.True(result["app"].Environment["LOG_LEVEL"].IsLiteral);
+        Assert.Equal("info", result["app"].Environment["LOG_LEVEL"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParseEnvironment_ArrayFormatWithoutValue_SetsEmptyString()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      - DEBUG
+      - LOG_LEVEL=info
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["app"].Environment.Count);
+        Assert.True(result["app"].Environment["DEBUG"].IsLiteral);
+        Assert.Equal(string.Empty, result["app"].Environment["DEBUG"].LiteralValue);
+        Assert.True(result["app"].Environment["LOG_LEVEL"].IsLiteral);
+        Assert.Equal("info", result["app"].Environment["LOG_LEVEL"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParseEnvironment_WithPlaceholders_CreatesFormattedString()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      - DATABASE_URL=${DATABASE_URL}
+      - PORT=8080
+      - DB_HOST=${DB_HOST:-localhost}
+      - API_KEY=${API_KEY:?required}
+      - DEBUG=true
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // All environment variables should be included
+        Assert.Equal(5, result["app"].Environment.Count);
+        
+        // Literal values
+        Assert.True(result["app"].Environment["PORT"].IsLiteral);
+        Assert.Equal("8080", result["app"].Environment["PORT"].LiteralValue);
+        Assert.True(result["app"].Environment["DEBUG"].IsLiteral);
+        Assert.Equal("true", result["app"].Environment["DEBUG"].LiteralValue);
+        
+        // Placeholder values
+        Assert.False(result["app"].Environment["DATABASE_URL"].IsLiteral);
+        Assert.Equal("{0}", result["app"].Environment["DATABASE_URL"].Format);
+        Assert.Single(result["app"].Environment["DATABASE_URL"].Placeholders);
+        Assert.Equal("DATABASE_URL", result["app"].Environment["DATABASE_URL"].Placeholders[0].Name);
+        Assert.Null(result["app"].Environment["DATABASE_URL"].Placeholders[0].DefaultValue);
+        
+        Assert.False(result["app"].Environment["DB_HOST"].IsLiteral);
+        Assert.Equal("{0}", result["app"].Environment["DB_HOST"].Format);
+        Assert.Single(result["app"].Environment["DB_HOST"].Placeholders);
+        Assert.Equal("DB_HOST", result["app"].Environment["DB_HOST"].Placeholders[0].Name);
+        Assert.Equal("localhost", result["app"].Environment["DB_HOST"].Placeholders[0].DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.ColonMinus, result["app"].Environment["DB_HOST"].Placeholders[0].DefaultType);
+        
+        Assert.False(result["app"].Environment["API_KEY"].IsLiteral);
+        Assert.Equal("{0}", result["app"].Environment["API_KEY"].Format);
+        Assert.Single(result["app"].Environment["API_KEY"].Placeholders);
+        Assert.Equal("API_KEY", result["app"].Environment["API_KEY"].Placeholders[0].Name);
+        
+        // Check placeholders dictionary
+        Assert.Equal(3, result["app"].Placeholders.Count);
+        Assert.Contains("DATABASE_URL", result["app"].Placeholders.Keys);
+        Assert.Contains("DB_HOST", result["app"].Placeholders.Keys);
+        Assert.Contains("API_KEY", result["app"].Placeholders.Keys);
+    }
+
+    [Fact]
+    public void ParseEnvironment_WithEscapedPlaceholders_IncludesLiteralValues()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      - LITERAL=$${VARIABLE}
+      - NORMAL=value
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Escaped placeholders ($$) should be treated as literal values
+        Assert.Equal(2, result["app"].Environment.Count);
+        Assert.True(result["app"].Environment["LITERAL"].IsLiteral);
+        Assert.Equal("${VARIABLE}", result["app"].Environment["LITERAL"].LiteralValue);
+        Assert.True(result["app"].Environment["NORMAL"].IsLiteral);
+        Assert.Equal("value", result["app"].Environment["NORMAL"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParseEnvironment_DictionaryWithPlaceholders_CreatesFormattedString()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      DATABASE_URL: ${DATABASE_URL}
+      PORT: 8080
+      CONFIG_PATH: ${CONFIG_PATH:-/etc/config}
+      DEBUG: true
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // All environment variables should be included
+        Assert.Equal(4, result["app"].Environment.Count);
+        
+        // Literal values
+        Assert.True(result["app"].Environment["PORT"].IsLiteral);
+        Assert.Equal("8080", result["app"].Environment["PORT"].LiteralValue);
+        Assert.True(result["app"].Environment["DEBUG"].IsLiteral);
+        Assert.Equal("true", result["app"].Environment["DEBUG"].LiteralValue);
+        
+        // Placeholder values
+        Assert.False(result["app"].Environment["DATABASE_URL"].IsLiteral);
+        Assert.Single(result["app"].Environment["DATABASE_URL"].Placeholders);
+        Assert.Equal("DATABASE_URL", result["app"].Environment["DATABASE_URL"].Placeholders[0].Name);
+        
+        Assert.False(result["app"].Environment["CONFIG_PATH"].IsLiteral);
+        Assert.Single(result["app"].Environment["CONFIG_PATH"].Placeholders);
+        Assert.Equal("CONFIG_PATH", result["app"].Environment["CONFIG_PATH"].Placeholders[0].Name);
+        Assert.Equal("/etc/config", result["app"].Environment["CONFIG_PATH"].Placeholders[0].DefaultValue);
+    }
+
+    [Fact]
+    public void ParseEnvironment_WithPartialPlaceholders_CreatesFormattedString()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    environment:
+      - URL=http://${HOST}:8080/api
+      - PURE=literal
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Both variables should be included
+        Assert.Equal(2, result["app"].Environment.Count);
+        
+        // URL contains placeholder in the middle
+        Assert.False(result["app"].Environment["URL"].IsLiteral);
+        Assert.Equal("http://{0}:8080/api", result["app"].Environment["URL"].Format);
+        Assert.Single(result["app"].Environment["URL"].Placeholders);
+        Assert.Equal("HOST", result["app"].Environment["URL"].Placeholders[0].Name);
+        
+        // PURE is literal
+        Assert.True(result["app"].Environment["PURE"].IsLiteral);
+        Assert.Equal("literal", result["app"].Environment["PURE"].LiteralValue);
+    }
+
+    #endregion
+
+    #region Placeholder Parsing Tests
+    // Comprehensive tests for parsing Docker Compose placeholder expressions
+    // Spec: https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation
+
+    [Theory]
+    [InlineData("${VAR}", "VAR", null)]
+    [InlineData("${DATABASE_URL}", "DATABASE_URL", null)]
+    [InlineData("${MY_VAR_123}", "MY_VAR_123", null)]
+    public void ParsePlaceholder_SimpleSyntax_ParsesCorrectly(string input, string expectedName, string? expectedDefault)
+    {
+        var yaml = $@"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - TEST={input}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        var placeholder = result["app"].Environment["TEST"].Placeholders[0];
+        
+        Assert.Equal(expectedName, placeholder.Name);
+        Assert.Equal(expectedDefault, placeholder.DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.None, placeholder.DefaultType);
+    }
+
+    [Theory]
+    [InlineData("${VAR:-default}", "VAR", "default")]
+    [InlineData("${DATABASE_URL:-postgres://localhost:5432}", "DATABASE_URL", "postgres://localhost:5432")]
+    [InlineData("${PORT:-8080}", "PORT", "8080")]
+    [InlineData("${EMPTY:-}", "EMPTY", "")]
+    [InlineData("${PATH:-/var/lib/data}", "PATH", "/var/lib/data")]
+    public void ParsePlaceholder_ColonMinusSyntax_ParsesDefaultValue(string input, string expectedName, string expectedDefault)
+    {
+        var yaml = $@"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - TEST={input}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        var placeholder = result["app"].Environment["TEST"].Placeholders[0];
+        
+        Assert.Equal(expectedName, placeholder.Name);
+        Assert.Equal(expectedDefault, placeholder.DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.ColonMinus, placeholder.DefaultType);
+    }
+
+    [Theory]
+    [InlineData("${VAR-default}", "VAR", "default")]
+    [InlineData("${DATABASE-postgres://localhost}", "DATABASE", "postgres://localhost")]
+    [InlineData("${CONFIG_FILE-config.yaml}", "CONFIG_FILE", "config.yaml")]
+    public void ParsePlaceholder_MinusSyntax_ParsesDefaultValue(string input, string expectedName, string expectedDefault)
+    {
+        var yaml = $@"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - TEST={input}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        var placeholder = result["app"].Environment["TEST"].Placeholders[0];
+        
+        Assert.Equal(expectedName, placeholder.Name);
+        Assert.Equal(expectedDefault, placeholder.DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.Minus, placeholder.DefaultType);
+    }
+
+    [Theory]
+    [InlineData("${VAR:?error message}")]
+    [InlineData("${VAR?error}")]
+    [InlineData("${REQUIRED:?This variable is required}")]
+    [InlineData("${API_KEY?}")]
+    public void ParsePlaceholder_RequiredSyntax_ParsesAsNoDefault(string input)
+    {
+        var yaml = $@"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - TEST={input}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        var envVar = result["app"].Environment["TEST"];
+        
+        Assert.False(envVar.IsLiteral);
+        Assert.Single(envVar.Placeholders);
+        // Required placeholders are treated as having no default
+        Assert.Null(envVar.Placeholders[0].DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.None, envVar.Placeholders[0].DefaultType);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_EscapedDollarSign_TreatsAsLiteral()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - TEST=$${VARIABLE}
+      - MIXED=before$${ESCAPED}after
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Escaped $$ should become literal ${VARIABLE}
+        Assert.True(result["app"].Environment["TEST"].IsLiteral);
+        Assert.Equal("${VARIABLE}", result["app"].Environment["TEST"].LiteralValue);
+        
+        Assert.True(result["app"].Environment["MIXED"].IsLiteral);
+        Assert.Equal("before${ESCAPED}after", result["app"].Environment["MIXED"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_MultiplePlaceholdersInValue_ParsesAll()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - CONN=postgres://${DB_USER:-admin}:${DB_PASS}@${DB_HOST:-localhost}:${DB_PORT:-5432}/${DB_NAME}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        var envVar = result["app"].Environment["CONN"];
+        
+        Assert.False(envVar.IsLiteral);
+        Assert.Equal("postgres://{0}:{1}@{2}:{3}/{4}", envVar.Format);
+        Assert.Equal(5, envVar.Placeholders.Count);
+        
+        Assert.Equal("DB_USER", envVar.Placeholders[0].Name);
+        Assert.Equal("admin", envVar.Placeholders[0].DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.ColonMinus, envVar.Placeholders[0].DefaultType);
+        
+        Assert.Equal("DB_PASS", envVar.Placeholders[1].Name);
+        Assert.Null(envVar.Placeholders[1].DefaultValue);
+        
+        Assert.Equal("DB_HOST", envVar.Placeholders[2].Name);
+        Assert.Equal("localhost", envVar.Placeholders[2].DefaultValue);
+        
+        Assert.Equal("DB_PORT", envVar.Placeholders[3].Name);
+        Assert.Equal("5432", envVar.Placeholders[3].DefaultValue);
+        
+        Assert.Equal("DB_NAME", envVar.Placeholders[4].Name);
+        Assert.Null(envVar.Placeholders[4].DefaultValue);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_MixedLiteralAndPlaceholder_PreservesLiterals()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - URL=https://${HOST}:${PORT}/api/v1
+      - PREFIX=app_${ENVIRONMENT}_suffix
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        var url = result["app"].Environment["URL"];
+        Assert.False(url.IsLiteral);
+        Assert.Equal("https://{0}:{1}/api/v1", url.Format);
+        Assert.Equal(2, url.Placeholders.Count);
+        Assert.Equal("HOST", url.Placeholders[0].Name);
+        Assert.Equal("PORT", url.Placeholders[1].Name);
+        
+        var prefix = result["app"].Environment["PREFIX"];
+        Assert.False(prefix.IsLiteral);
+        Assert.Equal("app_{0}_suffix", prefix.Format);
+        Assert.Single(prefix.Placeholders);
+        Assert.Equal("ENVIRONMENT", prefix.Placeholders[0].Name);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_MalformedPlaceholder_TreatsAsLiteral()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - MALFORMED=${NO_CLOSE_BRACE
+      - JUST_DOLLAR=$VAR
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Malformed placeholders should be treated as literal text
+        Assert.True(result["app"].Environment["MALFORMED"].IsLiteral);
+        Assert.Equal("${NO_CLOSE_BRACE", result["app"].Environment["MALFORMED"].LiteralValue);
+        
+        Assert.True(result["app"].Environment["JUST_DOLLAR"].IsLiteral);
+        Assert.Equal("$VAR", result["app"].Environment["JUST_DOLLAR"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_EmptyPlaceholder_TreatsAsLiteral()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - EMPTY=${}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Empty placeholder should be treated as literal
+        Assert.True(result["app"].Environment["EMPTY"].IsLiteral);
+        Assert.Equal("${}", result["app"].Environment["EMPTY"].LiteralValue);
+    }
+
+    [Fact]
+    public void ParsePlaceholder_UniquePlaceholdersAcrossMultipleEnvVars_TracksAll()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: test
+    environment:
+      - VAR1=${PLACEHOLDER1}
+      - VAR2=${PLACEHOLDER2:-default}
+      - VAR3=${PLACEHOLDER1}
+      - VAR4=literal
+      - VAR5=${PLACEHOLDER3}:${PLACEHOLDER1}
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        // Should have 3 unique placeholders even though PLACEHOLDER1 appears 3 times
+        Assert.Equal(3, result["app"].Placeholders.Count);
+        Assert.Contains("PLACEHOLDER1", result["app"].Placeholders.Keys);
+        Assert.Contains("PLACEHOLDER2", result["app"].Placeholders.Keys);
+        Assert.Contains("PLACEHOLDER3", result["app"].Placeholders.Keys);
+        
+        // Check that PLACEHOLDER2 has its default value
+        Assert.Equal("default", result["app"].Placeholders["PLACEHOLDER2"].DefaultValue);
+        Assert.Equal(PlaceholderDefaultType.ColonMinus, result["app"].Placeholders["PLACEHOLDER2"].DefaultType);
+    }
+
+    #endregion
+
+    #region Port Mapping Tests
+    // Spec: Ports can be defined using short syntax (string) or long syntax (mapping)
+
+    [Fact]
+    public void ParsePorts_ShortSyntax_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - ""8080:80""
+      - ""443:443/tcp""
+      - ""53:53/udp""
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(3, result["web"].Ports.Count);
+        
+        // First port: "8080:80" - no protocol specified
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Equal(8080, result["web"].Ports[0].Published);
+        Assert.Null(result["web"].Ports[0].Protocol); // Not explicitly specified
+        
+        // Second port: "443:443/tcp" - tcp explicitly specified
+        Assert.Equal(443, result["web"].Ports[1].Target);
+        Assert.Equal(443, result["web"].Ports[1].Published);
+        Assert.Equal("tcp", result["web"].Ports[1].Protocol);
+        
+        // Third port: "53:53/udp" - udp explicitly specified
+        Assert.Equal(53, result["web"].Ports[2].Target);
+        Assert.Equal(53, result["web"].Ports[2].Published);
+        Assert.Equal("udp", result["web"].Ports[2].Protocol);
+    }
+
+    [Fact]
+    public void ParsePorts_ShortSyntaxWithHostIp_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - ""127.0.0.1:8080:80""
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["web"].Ports);
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Equal(8080, result["web"].Ports[0].Published);
+        Assert.Equal("127.0.0.1", result["web"].Ports[0].HostIp);
+        Assert.Null(result["web"].Ports[0].Protocol); // Not explicitly specified
+    }
+
+    [Fact]
+    public void ParsePorts_LongSyntax_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - target: 80
+        published: 8080
+        protocol: tcp
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["web"].Ports);
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Equal(8080, result["web"].Ports[0].Published);
+        Assert.Equal("tcp", result["web"].Ports[0].Protocol);
+    }
+
+    [Fact]
+    public void ParsePorts_LongSyntaxWithUdp_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  dns:
+    image: dns-server
+    ports:
+      - target: 53
+        published: 5353
+        protocol: udp
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["dns"].Ports);
+        Assert.Equal(53, result["dns"].Ports[0].Target);
+        Assert.Equal(5353, result["dns"].Ports[0].Published);
+        Assert.Equal("udp", result["dns"].Ports[0].Protocol);
+    }
+
+    [Fact]
+    public void ParsePorts_LongSyntaxWithHostIp_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - target: 80
+        published: 8080
+        host_ip: 127.0.0.1
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["web"].Ports);
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Equal(8080, result["web"].Ports[0].Published);
+        Assert.Equal("127.0.0.1", result["web"].Ports[0].HostIp);
+        Assert.Null(result["web"].Ports[0].Protocol); // Protocol not explicitly specified in long syntax
+    }
+
+    [Fact]
+    public void ParsePorts_ContainerPortOnly_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - ""80""
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["web"].Ports);
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Null(result["web"].Ports[0].Published); // Not specified, will be randomly assigned
+        Assert.Null(result["web"].Ports[0].Protocol); // Not explicitly specified
+    }
+
+    [Fact]
+    public void ParsePorts_LongSyntaxWithName_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    ports:
+      - name: web
+        target: 80
+        published: 8080
+      - name: web-secured
+        target: 443
+        published: 8443
+        protocol: tcp
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["web"].Ports.Count);
+        
+        // First port with name
+        Assert.Equal(80, result["web"].Ports[0].Target);
+        Assert.Equal(8080, result["web"].Ports[0].Published);
+        Assert.Equal("web", result["web"].Ports[0].Name);
+        
+        // Second port with name
+        Assert.Equal(443, result["web"].Ports[1].Target);
+        Assert.Equal(8443, result["web"].Ports[1].Published);
+        Assert.Equal("web-secured", result["web"].Ports[1].Name);
+    }
+
+    #endregion
+
+    #region Volume Tests
+    // Spec: Volumes can be defined using short syntax (string) or long syntax (mapping)
+
+    [Fact]
+    public void ParseVolumes_ShortSyntaxBindMount_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    volumes:
+      - ./data:/app/data
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Volumes);
+        var volume = result["app"].Volumes[0];
+        Assert.Equal("./data", volume.Source);
+        Assert.Equal("/app/data", volume.Target);
+        Assert.Equal("bind", volume.Type);
+        Assert.False(volume.ReadOnly);
+    }
+
+    [Fact]
+    public void ParseVolumes_ShortSyntaxReadOnly_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    volumes:
+      - ./config:/app/config:ro
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Volumes);
+        var volume = result["app"].Volumes[0];
+        Assert.Equal("./config", volume.Source);
+        Assert.Equal("/app/config", volume.Target);
+        Assert.True(volume.ReadOnly);
+    }
+
+    [Fact]
+    public void ParseVolumes_ShortSyntaxNamedVolume_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  db:
+    image: postgres
+    volumes:
+      - dbdata:/var/lib/postgresql/data
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["db"].Volumes);
+        var volume = result["db"].Volumes[0];
+        Assert.Equal("dbdata", volume.Source);
+        Assert.Equal("/var/lib/postgresql/data", volume.Target);
+        Assert.Equal("volume", volume.Type);
+    }
+
+    [Fact]
+    public void ParseVolumes_ShortSyntaxAnonymous_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    volumes:
+      - /app/node_modules
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Volumes);
+        var volume = result["app"].Volumes[0];
+        Assert.Null(volume.Source);
+        Assert.Equal("/app/node_modules", volume.Target);
+        Assert.Equal("volume", volume.Type);
+    }
+
+    [Fact]
+    public void ParseVolumes_LongSyntax_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    volumes:
+      - type: bind
+        source: ./data
+        target: /app/data
+        read_only: true
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Volumes);
+        var volume = result["app"].Volumes[0];
+        Assert.Equal("bind", volume.Type);
+        Assert.Equal("./data", volume.Source);
+        Assert.Equal("/app/data", volume.Target);
+        Assert.True(volume.ReadOnly);
+    }
+
+    #endregion
+
+    #region Build Configuration Tests
+    // Spec: Build can be a string (context path) or an object with additional options
+
+    [Fact]
+    public void ParseBuild_ShortSyntax_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    build: ./app
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.NotNull(result["app"].Build);
+        Assert.Equal("./app", result["app"].Build!.Context);
+    }
+
+    [Fact]
+    public void ParseBuild_LongSyntax_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    build:
+      context: ./app
+      dockerfile: Dockerfile.prod
+      target: production
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        var build = result["app"].Build;
+        Assert.NotNull(build);
+        Assert.Equal("./app", build.Context);
+        Assert.Equal("Dockerfile.prod", build.Dockerfile);
+        Assert.Equal("production", build.Target);
+    }
+
+    [Fact]
+    public void ParseBuild_WithArgs_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    build:
+      context: .
+      args:
+        NODE_VERSION: '18'
+        BUILD_ENV: production
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        var build = result["app"].Build;
+        Assert.NotNull(build);
+        Assert.Equal(2, build.Args.Count);
+        Assert.Equal("18", build.Args["NODE_VERSION"]);
+        Assert.Equal("production", build.Args["BUILD_ENV"]);
+    }
+
+    [Fact]
+    public void ParseBuild_WithArgsArray_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    build:
+      context: .
+      args:
+        - NODE_VERSION=18
+        - BUILD_ENV=production
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        var build = result["app"].Build;
+        Assert.NotNull(build);
+        Assert.Equal(2, build.Args.Count);
+        Assert.Equal("18", build.Args["NODE_VERSION"]);
+        Assert.Equal("production", build.Args["BUILD_ENV"]);
+    }
+
+    #endregion
+
+    #region Command and Entrypoint Tests
+    // Spec: Command and entrypoint can be a string or an array
+
+    [Fact]
+    public void ParseCommand_String_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    command: bundle exec thin -p 3000
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Command);
+        Assert.Equal("bundle exec thin -p 3000", result["app"].Command[0]);
+    }
+
+    [Fact]
+    public void ParseCommand_Array_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    command: [""bundle"", ""exec"", ""thin"", ""-p"", ""3000""]
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(5, result["app"].Command.Count);
+        Assert.Equal("bundle", result["app"].Command[0]);
+        Assert.Equal("3000", result["app"].Command[4]);
+    }
+
+    [Fact]
+    public void ParseEntrypoint_String_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    entrypoint: /app/start.sh
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Single(result["app"].Entrypoint);
+        Assert.Equal("/app/start.sh", result["app"].Entrypoint[0]);
+    }
+
+    [Fact]
+    public void ParseEntrypoint_Array_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  app:
+    image: myapp
+    entrypoint: [""/app/start.sh"", ""--verbose""]
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["app"].Entrypoint.Count);
+        Assert.Equal("/app/start.sh", result["app"].Entrypoint[0]);
+        Assert.Equal("--verbose", result["app"].Entrypoint[1]);
+    }
+
+    #endregion
+
+    #region Dependencies Tests
+    // Spec: depends_on can be an array of service names or an object with conditions
+
+    [Fact]
+    public void ParseDependsOn_SimpleArray_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    depends_on:
+      - db
+      - cache
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["web"].DependsOn.Count);
+        Assert.True(result["web"].DependsOn.ContainsKey("db"));
+        Assert.True(result["web"].DependsOn.ContainsKey("cache"));
+        Assert.Equal("service_started", result["web"].DependsOn["db"].Condition);
+    }
+
+    [Fact]
+    public void ParseDependsOn_WithConditions_ParsesCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx
+    depends_on:
+      db:
+        condition: service_healthy
+      cache:
+        condition: service_started
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result["web"].DependsOn.Count);
+        Assert.Equal("service_healthy", result["web"].DependsOn["db"].Condition);
+        Assert.Equal("service_started", result["web"].DependsOn["cache"].Condition);
+    }
+
+    #endregion
+
+    #region Multiple Services Tests
+
+    [Fact]
+    public void ParseComposeFile_MultipleServices_ParsesAllCorrectly()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  web:
+    image: nginx:alpine
+    ports:
+      - ""80:80""
+  
+  api:
+    build: ./api
+    environment:
+      NODE_ENV: production
+    depends_on:
+      - db
+  
+  db:
+    image: postgres:15
+    volumes:
+      - dbdata:/var/lib/postgresql/data
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(3, result.Count);
+        
+        // Verify web service
+        Assert.Equal("nginx:alpine", result["web"].Image);
+        Assert.Single(result["web"].Ports);
+        
+        // Verify api service
+        Assert.NotNull(result["api"].Build);
+        Assert.Equal("./api", result["api"].Build!.Context);
+        Assert.Single(result["api"].Environment);
+        Assert.Single(result["api"].DependsOn);
+        
+        // Verify db service
+        Assert.Equal("postgres:15", result["db"].Image);
+        Assert.Single(result["db"].Volumes);
+    }
+
+    #endregion
+
+    #region Service Name Case Sensitivity Tests
+
+    [Fact]
+    public void ParseComposeFile_ServiceNames_AreCaseInsensitive()
+    {
+        var yaml = @"
+version: '3.8'
+services:
+  WEB:
+    image: nginx
+  Api:
+    image: node
+";
+        var result = DockerComposeParser.ParseComposeFile(yaml);
+        
+        Assert.Equal(2, result.Count);
+        Assert.True(result.ContainsKey("WEB"));
+        Assert.True(result.ContainsKey("web")); // Case insensitive
+        Assert.True(result.ContainsKey("Api"));
+        Assert.True(result.ContainsKey("api")); // Case insensitive
+    }
+
+    #endregion
+}
diff --git a/tests/Aspire.Hosting.Docker.Tests/test-docker-compose.yml b/tests/Aspire.Hosting.Docker.Tests/test-docker-compose.yml
new file mode 100644
index 00000000000..82f00e9c646
--- /dev/null
+++ b/tests/Aspire.Hosting.Docker.Tests/test-docker-compose.yml
@@ -0,0 +1,51 @@
+version: '3.8'
+
+services:
+  frontend:
+    build:
+      context: ./frontend
+      dockerfile: Dockerfile
+    ports:
+      - "3000:3000"
+    environment:
+      - VITE_API_URL=http://localhost:8000
+    volumes:
+      - ./frontend:/app
+      - /app/node_modules
+    depends_on:
+      - backend
+
+  backend:
+    build:
+      context: ./backend
+      dockerfile: Dockerfile
+    ports:
+      - "8000:8000"
+    environment:
+      - DATABASE_URL=postgresql://user:password@db:5432/appdb
+    volumes:
+      - ./backend:/app
+    depends_on:
+      - db
+
+  db:
+    image: postgres:15
+    environment:
+      - POSTGRES_USER=user
+      - POSTGRES_PASSWORD=password
+      - POSTGRES_DB=appdb
+    ports:
+      - "5432:5432"
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+
+  redis:
+    image: redis:7-alpine
+    ports:
+      - "6379:6379"
+    volumes:
+      - redis_data:/data
+
+volumes:
+  postgres_data:
+  redis_data: