
Research
/Security News
Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.
@mittwald/container-deploy
Advanced tools
Reusable orchestration and API adapters for building and deploying containerized projects.
Reusable orchestration and API adapters for building and deploying containerized projects.
container-deploy is a TypeScript library that provides a unified interface for orchestrating Docker-based application deployments with the Mittwald API. It handles the complete deployment pipeline including Docker image building, registry management, and service deployment.
The library is organized around a three-tier architecture that cleanly separates concerns and enables flexible composition:
This architecture enables both high-level deployment (deployProject()) and fine-grained control over individual operations.
npm install container-deploy
┌─────────────────────────────────────────────────────┐
│ Tier 3: Consumers │
│ (CLI, CI/CD, external tools use either tier below) │
└──────────┬──────────────────────────────────────────┘
│
┌────┴─────────────────────────────────┐
│ │
┌─────▼──────────────────────┐ ┌────────────▼──────────────────┐
│ Tier 2: Orchestration │ │ Tier 1: Entities (Direct) │
│ Multi-step workflows │ │ Single-responsibility ops │
│ (deploy_project.ts, │ │ (registry.ts, domain.ts, │
│ registry_setup.ts) │ │ service.ts, docker.ts) │
└────────────┬───────────────┘ │ │
│ │ Can be used independently │
┌─────▼──────────────────┤ for fine-grained control │
│ │ │
│ Compose & delegate └──────────────────────────────┘
│
┌──────▼─────────────────────────────────────────────────────┐
│ External Dependencies (@mittwald/api-client, Docker CLI) │
└───────────────────────────────────────────────────────────┘
Pure API wrappers with no orchestration logic. Each handles a single domain:
entities/registry.ts – Container Registry APIgetProjectRegistry() – Fetch existing registry for a projectcreateRegistry() – Create new registry servicecheckProjectRegistry() – Verify registry existsregistry_setup.ts, direct consumers for registry-only operationsentities/service.ts – Service Deployment APIdeployService() – Deploy a standard service (returns full DeployRes)deployServiceAs(apiClient, projectId, serviceName, serviceConfig, timeout) – Deploy any named service with custom config (returns service ID string)registry_setup.ts for registry service, deploy_project.ts for application servicedeployServiceAs() to create the container registry serviceentities/domain.ts – Domain & Ingress APIcreateDomain() – Create ingress for a servicewaitForDomainReachable() – Poll until domain IPs assigned and TLS certificate createdwaitForIngressReady() – Semantic alias for clarity in orchestration contextscreateAndWaitForDomain() – Combine domain creation and waitingregistry_setup.ts to expose registry, direct consumers for domain operationsentities/docker.ts – Docker OperationscheckDocker() – Verify Docker is installed and runningcheckRailpack() – Verify Railpack build tool is availablelocalDockerBuild() – Build Docker image locallylocalBuildWithRailpack() – Build with Mittwald's Railpack toolbuildDockerImage() – Smart build selection (Railpack if available, else Docker)localDockerPush() – Push image to registrydeploy_project.ts for image building/pushingMulti-step workflows that compose entities to achieve higher-level goals:
orchestration/registry_setup.ts – Complete Registry OrchestrationsetupProjectRegistry() executes 6 steps:
service.tsdomain.tsregistry.tsorchestration/deploy_project.ts – Complete Project DeploymentdeployProject() executes sequence:
registry_setup.tsdocker.tsdocker.tsservice.tsimport { deployProject, Duration } from 'container-deploy';
// Deploy entire project (registry setup + build + push + service)
const result = await deployProject({
apiClient: myApiClient,
projectId: 'your-project-id',
waitTimeout: Duration.fromSeconds(600),
});
console.log(`✓ Deployed: ${result.serviceName} (${result.deployedServiceId})`);
import { setupProjectRegistry } from 'container-deploy';
import { buildDockerImage, localDockerPush } from 'container-deploy';
// Set up registry with custom timeout
const registry = await setupProjectRegistry(
apiClient,
projectId,
Duration.fromMinutes(5)
);
// Then handle build/push with your own logic
await buildDockerImage(buildDir, registry.username, registry.password);
await localDockerPush(imageName, registry.host, registry.username, registry.password);
import {
checkProjectRegistry,
createRegistry,
createDomain,
waitForDomainReachable,
} from 'container-deploy';
// Check existing registry
const existing = await checkProjectRegistry(apiClient, projectId);
if (!existing) {
// Create registry service
const serviceId = await deployServiceAs(apiClient, projectId, 'registry', {...});
// Expose via domain
const domain = await createDomain(apiClient, serviceId, {...});
await waitForDomainReachable(apiClient, domain.id, Duration.fromMinutes(2));
}
deployProject(options: DeployOptions): Promise<DeployResult>Complete deployment pipeline: registry setup → Docker build → push → service deployment.
Parameters:
apiClient – Mittwald API v2 client instanceprojectId – UUID of target projectwaitTimeout – Maximum time to wait for operationsReturns:
{
deployedServiceId: string; // ID of deployed service
serviceName: string; // Service name
}
setupProjectRegistry(apiClient, projectId, timeout): Promise<RegistryData>Set up complete registry infrastructure (service + domain + registration).
Parameters:
apiClient – Mittwald API v2 client instanceprojectId – UUID of target projecttimeout – Maximum time to wait for registry readinessReturns:
{
id: string; // Registry service ID
projectId: string; // Project UUID
host: string; // Registry hostname/domain
username: string; // Registry username
password: string; // Registry password
source: 'existing' | 'created'; // Whether newly created or already existed
}
import { buildDockerImage, localDockerPush, checkDocker } from 'container-deploy';
// Check Docker availability
await checkDocker();
// Build image (auto-selects Railpack or Docker)
await buildDockerImage(buildDir, dockerUsername, dockerPassword);
// Push to registry
await localDockerPush(imageName, registryHost, username, password);
import {
// Registry
getProjectRegistry,
createRegistry,
checkProjectRegistry,
// Service
deployService,
deployServiceAs,
// Domain
createDomain,
waitForDomainReachable,
createAndWaitForDomain,
} from 'container-deploy';
// Example: Create registry domain
const domain = await createAndWaitForDomain(
apiClient,
serviceId,
{ ingressName: 'my-registry', tlsEnabled: true },
Duration.fromMinutes(3)
);
DurationFlexible duration handling for timeouts and scheduling:
import { Duration } from 'container-deploy';
// Creation methods
const dur1 = Duration.fromSeconds(30);
const dur2 = Duration.fromMilliseconds(5000);
const dur3 = Duration.fromZero();
// Calculations
const combined = dur1.add(dur2);
const futureDate = dur1.fromNow();
const comparison = dur1.compare(dur2);
// Conversion
console.log(dur1.seconds); // 30
console.log(dur1.milliseconds); // 30000
import { generatePassword, generatePasswordWithSpecialChars } from 'container-deploy';
// Basic password (32 chars, alphanumeric)
const password = generatePassword();
// With special characters (32 chars, 4 special)
const securePassword = generatePasswordWithSpecialChars(32, 4);
Each entity module handles exactly one domain:
This separation enables:
Entity operations are stateless and composable. Orchestration modules (registry_setup.ts, deploy_project.ts) coordinate them:
User Request
↓
Orchestration Module ← Decides flow & manages state
↓
├→ Entity 1 ← Pure operation (Get → Transform → Return)
├→ Entity 2 ← Pure operation (Post → Wait → Return)
└→ Entity 3 ← Pure operation (Validate → Transform → Return)
↓
Result
This allows:
All operations that require polling use waitUntil() helper with exponential backoff:
// Example from domain.ts
await waitUntil(
() => isIngressReady(ingress), // Poll condition
Duration.fromSeconds(1), // Initial wait
Duration.fromMinutes(3), // Max total time
);
Critical known issue: Registry setup includes hardcoded 2-minute wait before API registration (documented Mittwald behavior for DNS/TLS propagation). This prevents race conditions when domain is created but not yet globally available.
All public functions are fully typed. Internal helper types are in src/types/index.ts:
export interface RegistryData {
id: string;
projectId: string;
host?: string; // Optional because checkProjectRegistry doesn't populate
username: string;
password: string;
source: 'existing' | 'created';
}
export interface DeployOptions {
apiClient: MittwaldAPIV2Client;
projectId: string;
waitTimeout: Duration;
}
The optional host field in RegistryData is intentional – different code paths populate different fields.
src/orchestration/my_flow.tssrc/index.tsExample: If you need "registry + service without domain":
// src/orchestration/registry_and_service_setup.ts
import { createRegistry } from '../entities/registry';
import { deployServiceAs } from '../entities/service';
export async function setupRegistryAndService(
apiClient: MittwaldAPIV2Client,
projectId: string,
timeout: Duration
): Promise<{ registry: RegistryData; serviceId: string }> {
const registry = await createRegistry(apiClient, projectId);
const serviceId = await deployServiceAs(apiClient, projectId, 'app', {...});
return { registry, serviceId };
}
Current test suite validates primary deployProject() flow with mocked dependencies. See test/deploy.test.ts for patterns.
registry_setup.ts is necessary for Mittwald platformdocker.ts require local Docker installationThis package follows TypeScript strict mode and Jest testing conventions. Before committing:
npm run build # Compile TypeScript
npm run test # Run test suite (jest)
npm run lint # Type check (tsc --noEmit)
All new features should include:
src/types/index.ts if neededtest/src/
├── entities/ # Core domain entities
│ ├── docker.ts # Docker build configuration
│ ├── project.ts # Project metadata
│ ├── registry.ts # Registry setup and image operations
│ ├── repository.ts # Repository validation
│ └── service.ts # Service deployment logic
├── orchestration/ # High-level orchestration
│ └── deploy_project.ts # Main deployment orchestrator
├── types/ # TypeScript type definitions
│ └── index.ts # Core types (DeployOptions, DeployResult, etc.)
└── utils/ # Utility functions
└── helpers.ts # Duration, password generation, etc.
test/
└── deploy.test.ts # Integration tests for deployProject
npm install
npm run build
# One-time build
npm run build
# Watch mode for development
npm run build:watch
# Run all tests
npm test
# Watch mode
npm test -- --watch
Tests are configured with Jest and ts-jest for TypeScript support. Current test coverage includes full integration tests for the deployProject function.
This package can be tested directly from git branches before publishing to npm:
"@mittwald/container-deploy": "github:mittwald/container-deploy#branch-name"
For this to work, the dist folder must be committed after building. When released to npm, users receive the published version and don't need to worry about this.
DeployOptions{
apiClient: any; // Mittwald API client
projectId: string; // Project UUID
waitTimeout: Duration; // Deployment timeout
}
DeployResult{
deployedServiceId: string; // ID of deployed service
serviceName: string; // Name of deployed service
}
RegistryData{
username: string;
password: string;
uri: string;
host: string;
registryServiceId: string;
registry: any;
created?: boolean;
}
RepositoryData{
buildContext: string;
ports: string[];
dockerfilePath?: string;
dockerfileContent?: string;
dockerfileCreated?: boolean;
imageId?: string;
imageName?: string;
railpackPlanPath?: string | null;
}
The library uses sensible defaults:
%, _, -, +, &The deployment pipeline follows these steps:
MIT License – see LICENSE file for details
Lars Bergmann l.bergmann@mittwald.de
For more information or issues, please visit the GitHub repository.
FAQs
Reusable orchestration and API adapters for building and deploying containerized projects.
The npm package @mittwald/container-deploy receives a total of 281 weekly downloads. As such, @mittwald/container-deploy popularity was classified as not popular.
We found that @mittwald/container-deploy demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.

Research
/Security News
The North Korean malware loader hides in a Packagist-listed package and its GitHub branch to fetch and execute remote code in a likely Contagious Interview-style lure.

Security News
The Rust project is moving toward formal rules on LLM use in contributions after months of internal debate over maintainer burden, code quality, and contributor experience.