Skip to main content

Objective-C on Miren

Objective-C isn't auto-detected, so you deploy it with a Dockerfile.miren that compiles your app with GNUstep. This guide uses SOPE (the SKYRiX Object Publishing Environment — the Objective-C web framework that SOGo is built on), whose WOApplication and WOHttpAdaptor give you a real HTTP server, WebObjects-style.

Let your agent do this

Ask your AI coding agent to "set up this Objective-C app on Miren" after installing the Miren agent skills. It adds the Dockerfile.miren and GNUstep build, and deploys — using this page as its reference.

Do you need a Dockerfile?

Yes. Add a Dockerfile.miren to your project root. Miren builds from it instead of guessing the stack — see Using Dockerfile.miren.

Want native support?

Miren auto-detects and builds common stacks (Python, Node, Bun, Go, Ruby, Rust) without a Dockerfile. This language isn't one of them yet — if you'd like first-class support, request it.

The app

Subclass WOApplication and override dispatchRequest: to return a WOResponse. WOApplicationMain starts the app and its built-in HTTP adaptor:

#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOResponse.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOCoreApplication.h>

@interface HelloApp : WOApplication
@end

@implementation HelloApp
- (WOResponse *)dispatchRequest:(WORequest *)_request {
WOResponse *r = [WOResponse responseWithRequest:_request];
[r setStatus:200];
[r setHeader:@"text/plain" forKey:@"content-type"];
[r appendContentString:@"Hello from Objective-C on Miren!\n"];
return r;
}
@end

int main(int argc, const char *argv[]) {
return WOApplicationMain(@"HelloApp", argc, argv);
}

Bind to the injected port

SOPE's HTTP adaptor takes its listen address from the WOPort default, which you pass on the command line. Miren injects PORT, so start the app with -WOPort 0.0.0.0:$PORT:

web: sh -c '. /usr/share/GNUstep/Makefiles/GNUstep.sh && exec /app/obj/app -WOPort 0.0.0.0:$PORT'
Give WOPort an explicit 0.0.0.0

-WOPort 8080 alone makes the adaptor bind the wildcard address (*:8080), which fails on this stack with NGCouldNotBindSocketException … Address family not supported. Pass the address explicitly — -WOPort 0.0.0.0:$PORT — so it binds IPv4 on all interfaces. The sh -c '. GNUstep.sh && exec …' wrapper sources the GNUstep environment so the app finds its frameworks at runtime.

The Dockerfile

Build with GNUstep's makefile system. gnustep-make provides only the makefiles, so install GNU make too; libsope-dev provides SOPE's headers and libraries. A single-stage image keeps the GNUstep runtime and SOPE libraries available at run time:

FROM debian:12
RUN apt-get update -y \
&& apt-get install -y make gobjc gnustep-make gnustep-base-runtime libgnustep-base-dev libsope-dev libsope1 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY main.m GNUmakefile ./
RUN . /usr/share/GNUstep/Makefiles/GNUstep.sh && make
EXPOSE 8080

The GNUmakefile builds a plain tool linked against the SOPE libraries (SOPE's own woapp.make bundle fragment is too old for current gnustep-make, so link them directly):

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = app
app_OBJC_FILES = main.m
app_TOOL_LIBS += -lNGObjWeb -lNGExtensions -lEOControl -lNGStreams -lNGMime -lSaxObjC -lDOM

include $(GNUSTEP_MAKEFILES)/tool.make

The compiled binary lands at obj/app.

.dockerignore

.git

Deploy

Create .miren/app.toml naming your app and deploy from your project root:

name = "objc-bench"
miren deploy
The Procfile is required

Even with a Dockerfile.miren, Miren needs at least one service defined — the web: line above. Without it the deploy stops with no services defined.

Agent quick reference

  • Detection: none — requires Dockerfile.miren
  • Framework: SOPE (libsope-dev) — WOApplication + WOHttpAdaptor; override dispatchRequest:
  • Build: GNUstep makefiles as a plain tool linking -lNGObjWeb … (install make); binary at obj/app
  • Port: -WOPort 0.0.0.0:$PORT — the explicit 0.0.0.0 avoids a wildcard bind failure
  • Runtime: source GNUstep.sh before exec so the app finds its frameworks
  • Service is required: the web: Procfile line — the image CMD is not used

Next steps