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.
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.
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'
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
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; overridedispatchRequest: - Build: GNUstep makefiles as a plain tool linking
-lNGObjWeb …(installmake); binary atobj/app - Port:
-WOPort 0.0.0.0:$PORT— the explicit0.0.0.0avoids a wildcard bind failure - Runtime: source
GNUstep.shbefore exec so the app finds its frameworks - Service is required: the
web:Procfile line — the imageCMDis not used
Next steps
- C on Miren — the C guide (Objective-C is a superset of C)
- Using Dockerfile.miren — how custom builds work
- Deployment — how deploys build and activate