diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..94143827ed065ca0d7d5be1b765d255c5c32cd9a
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+Dockerfile
diff --git a/Dockerfile b/Dockerfile
index 167dc5fef469d9408743aa303f21c4491fd8f5ec..55aed0442e1ec6610f7a679b6a7d19a898743dd8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -22,4 +22,4 @@ WORKDIR /coordinator
 RUN ./gradlew --no-daemon installDist
 
 ENV CONFIG_FILE /config.coordinator
-ENTRYPOINT /bin/bash -c "./build/install/coordinator/bin/coordinator $CONFIG_FILE"
+ENTRYPOINT ["/bin/bash", "./build/install/coordinator/bin/coordinator", "--", "$CONFIG_FILE"]
diff --git a/examples/ros3rag.coordinator b/examples/ros3rag.coordinator
index cba3b66b27a396d2340d2b80469ea7936433f7ba..5cd11c2b229025dc91ebc233e155e2a400c5dd96 100644
--- a/examples/ros3rag.coordinator
+++ b/examples/ros3rag.coordinator
@@ -1,6 +1,6 @@
 components {
-  component robotCtrlA, docker compose = "ros_place_a",      mqtt topic = "ros-place-a"
-  component robotCtrlB, docker compose = "ros_place_b",      mqtt topic = "ros-place-b"
+  component robotCtrlA, docker compose = "ros_place_a",      mqtt topic = "ros-place-a", status "ready" after 1 sec
+  component robotCtrlB, docker compose = "ros_place_b",      mqtt topic = "ros-place-b", status "ready" after 1 sec
   component ragA,       docker compose = "rag_place_a",      mqtt topic = "rag-a"
   component ragB,       docker compose = "rag_place_b",      mqtt topic = "rag-b"
   component random,     docker compose = "cgv_random_dummy", status "ready" after 1 sec, start as up
diff --git a/src/main/jastadd/Coordinator.flex b/src/main/jastadd/Coordinator.flex
index 2fd6396de3752da82780148e13896c31da029416..6129b5a9f2558b3065001e40c8e6afce2fe2362a 100644
--- a/src/main/jastadd/Coordinator.flex
+++ b/src/main/jastadd/Coordinator.flex
@@ -46,11 +46,16 @@ Comment = "//" [^\n\r]+
 "components"         { return sym(Terminals.COMPONENTS); }
 "component"          { return sym(Terminals.COMPONENT); }
 "docker compose"     { return sym(Terminals.DOCKER_COMPOSE); }
-"mqtt topic"         { return sym(Terminals.MQTT_TOPIC); }
-"status" { return sym(Terminals.STATUS); }
-"after" { return sym(Terminals.AFTER); }
-"sec" { return sym(Terminals.SEC); }
-"start as up" { return sym(Terminals.START_AS_UP); }
+"script"             { return sym(Terminals.SCRIPT); }
+"report"             { return sym(Terminals.REPORT); }
+"mqtt"               { return sym(Terminals.MQTT); }
+"status"             { return sym(Terminals.STATUS); }
+"after"              { return sym(Terminals.AFTER); }
+"sec"                { return sym(Terminals.SEC); }
+"in"                 { return sym(Terminals.IN); }
+"start"              { return sym(Terminals.START); }
+"using"              { return sym(Terminals.USING); }
+"after reqs"         { return sym(Terminals.AFTER_REQS); }
 
 "="                  { return sym(Terminals.EQUALS); }
 ","                  { return sym(Terminals.COMMA); }
@@ -60,7 +65,7 @@ Comment = "//" [^\n\r]+
 
 {Identifier}         { return sym(Terminals.NAME); }
 {Text}               { return symText(Terminals.TEXT); }
-{Integer}      { return sym(Terminals.INTEGER); }
+{Integer}            { return sym(Terminals.INTEGER); }
 <<EOF>>              { return sym(Terminals.EOF); }
 /* error fallback */
 [^]                  { throw new Error("Illegal character '"+ yytext() +"' at line " + (yyline+1) + " column " + (yycolumn+1) + " in state " + yystate()); }
diff --git a/src/main/jastadd/Coordinator.jrag b/src/main/jastadd/Coordinator.jrag
index 19fc3d47d6d9200986df4b397decd093173d0159..d2ac7291ea4c2bc928c8ff7b670a182ced9f72a6 100644
--- a/src/main/jastadd/Coordinator.jrag
+++ b/src/main/jastadd/Coordinator.jrag
@@ -51,30 +51,91 @@ aspect Manipulation {
     for (String service : services) {
       resolveComponentByDockerComposeName(service).ifPresentOrElse(
         comp -> result.add(comp),
-        () -> System.err.println("Could not resolve component for '" + service + "'!")
+        () -> System.err.println("Could not resolve docker component for '" + service + "'!")
       );
     }
     return result;
   }
 
-  public boolean Component.callDockerCompose() throws IOException, InterruptedException {
-    String[] args = { "docker-compose", "up", "-d", getDockerComposeName() };
+  //--- start ---
+  public abstract boolean StartStrategy.start() throws IOException, InterruptedException;
+  @Override
+  public boolean DockerComposeStrategy.start() throws IOException, InterruptedException {
+    if (!getName().isBlank()) {
+      String[] args = { "docker-compose", "up", "-d", getName() };
+      if (coordinator().DRY_RUN) {
+        System.out.println("Would start > " + java.util.Arrays.toString(args));
+        return true;
+      }
+      System.out.println("Starting " + Arrays.toString(args));
+      ProcessBuilder builder = new ProcessBuilder(args);
+
+      Process process = builder.start();
+      process.waitFor();
+
+      try (java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()))) {
+        System.out.println("Out of " + java.util.Arrays.toString(args) + ":\n" + reader.lines().collect(java.util.stream.Collectors.joining("\n")));
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+
+      try (java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getErrorStream()))) {
+        System.out.println("Err of " + java.util.Arrays.toString(args) + ":\n" + reader.lines().collect(java.util.stream.Collectors.joining("\n")));
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+
+      return process.exitValue() == 0;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean ScriptStrategy.start() throws IOException, InterruptedException {
+    String[] command = getCommand().split(" ");
     if (coordinator().DRY_RUN) {
-      System.out.println("Would start > " + java.util.Arrays.toString(args));
+      System.out.println("Would start script " + Arrays.toString(command));
       return true;
     }
-    ProcessBuilder builder = new ProcessBuilder(args);
+    System.out.println("Starting script " + Arrays.toString(command));
+    ProcessBuilder builder = new ProcessBuilder(command);
+    if (!getCwd().isBlank()) {
+      builder.directory(new java.io.File(getCwd()));
+    }
 
     Process process = builder.start();
-    process.waitFor();
-    return process.exitValue() == 0;
+    // Do not wait for process to be finished
+    return true;
   }
+
+  @Override
+  public boolean ManualStartStrategy.start() throws IOException, InterruptedException {
+    // do nothing
+    System.out.println("Component " + containingComponent().getName() + " has to be started");
+    return true;
+  }
+
 }
 
 aspect Navigation {
   inh Coordinator Component.coordinator();
   inh Coordinator ParsedPrecedenceRelation.coordinator();
   eq Coordinator.getChild().coordinator() = this;
+
+  inh Component StartStrategy.containingComponent();
+  eq Component.getStartStrategy().containingComponent() = this;
+
+  syn boolean StartStrategy.isDockerComposeStartStrategy() = false;
+  eq DockerComposeStartStrategy.isDockerComposeStartStrategy() = true;
+
+  syn boolean StartStrategy.asDockerComposeStartStrategy() = null;
+  eq DockerComposeStartStrategy.asDockerComposeStartStrategy() = this;
+
+  syn boolean ReportStrategy.isMqttReportStrategy() = false;
+  eq MqttReportStrategy.isMqttReportStrategy() = true;
+
+  syn boolean ReportStrategy.asMqttReportStrategy() = null;
+  eq MqttReportStrategy.asMqttReportStrategy() = this;
 }
 
 aspect Printing {
@@ -117,6 +178,7 @@ aspect Printing {
     return "Component " + getName() + " is " + status;
   }
 
+  //--- details ---
   syn String Coordinator.details() {
     StringBuilder sb = new StringBuilder();
     getComponentList().forEach(comp -> sb.append(comp.details()).append("\n"));
@@ -128,8 +190,8 @@ aspect Printing {
       sj.add(pred.getName());
     }
     return "<Name: " + getName() +
-      ", DockerComposeName: " + getDockerComposeName() +
-      ", MqttTopicPrefix: " + getMqttTopicPrefix() +
+      ", StartStrategy: " + getStartStrategy().details() +
+      ", ReportStrategy: " + getReportStrategy().details() +
       ", Status: " + getStatus() +
       ", NextCommand: " + getNextCommand() +
       ", Deps: " + sj.toString() +
@@ -140,6 +202,12 @@ aspect Printing {
   syn String AutoSetStatus.details() {
     return "[" + getStatus() + " after " + getDelayInSeconds() + " sec]";
   }
+  syn String StartStrategy.details();
+  eq DockerComposeStrategy.details() = "docker compose (name: " + getName() + ")";
+  eq ScriptStrategy.details() = "script (cmd: '" + getCommand() + "'" + (getCwd().isBlank() ? "" : "cwd: '" + getCwd() + "'") + ")";
+  eq ManualStartStrategy.details() = "manual";
+  syn String ReportStrategy.details();
+  eq MqttReportStrategy.details() = "mqtt (topic: " + getTopicPrefix() + ")";
 
   syn String ParsedPrecedenceRelation.prettyPrint() {
     StringBuilder sb = new StringBuilder();
diff --git a/src/main/jastadd/Coordinator.parser b/src/main/jastadd/Coordinator.parser
index 10ca5fb9018c603e9613a2798cd40b706b79d3ff..7ac691b9c2f920ccc174bfbc032c78a058962dcf 100644
--- a/src/main/jastadd/Coordinator.parser
+++ b/src/main/jastadd/Coordinator.parser
@@ -44,11 +44,21 @@ Component component =
   ;
 
 Component component_body =
-    COMMA DOCKER_COMPOSE EQUALS TEXT.dc component_body.c    {: c.setDockerComposeName(dc); return c; :}
-  | COMMA MQTT_TOPIC EQUALS TEXT.mqtt component_body.c      {: c.setMqttTopicPrefix(mqtt); return c; :}
+    COMMA START USING start_strategy.s component_body.c           {: c.setStartStrategy(s); return c; :}
+  | COMMA REPORT USING report_strategy.r component_body.c       {: c.setReportStrategy(r); return c; :}
   | COMMA STATUS TEXT.s AFTER INTEGER.i SEC component_body.c    {: c.setAutoSetStatus(new AutoSetStatus(s, Integer.parseInt(i))); return c; :}
-  | COMMA START_AS_UP component_body.c      {: c.setStartAsUp(true); return c; :}
-  |                                                         {: return new Component(); :}
+  | COMMA START AFTER_REQS component_body.c                          {: c.setStartAsUp(true); return c; :}
+  |                                                             {: Component c = new Component(); c.setStartStrategy(new ManualStartStrategy()); return c; :}
+  ;
+
+StartStrategy start_strategy =
+    DOCKER_COMPOSE TEXT.dc component_body.c    {: return new DockerComposeStrategy().setName(dc); :}
+  | SCRIPT TEXT.cmd                            {: return new ScriptStrategy().setCommand(cmd); :}
+  | SCRIPT TEXT.cmd IN TEXT.cwd                {: return new ScriptStrategy().setCommand(cmd).setCwd(cwd); :}
+  ;
+
+ReportStrategy report_strategy =
+    MQTT TEXT.topic component_body.c    {: return new ReportStrategy().setMqttTopicPrefix(topic); return c; :}
   ;
 
 StringList string_list =
diff --git a/src/main/jastadd/Coordinator.relast b/src/main/jastadd/Coordinator.relast
index 49d677a15cd8d64b6eab973834c02f4c539464cb..87baf3f82003358688c9298c98763e6f5a52e405 100644
--- a/src/main/jastadd/Coordinator.relast
+++ b/src/main/jastadd/Coordinator.relast
@@ -1,7 +1,15 @@
 Coordinator ::= Component* ParsedPrecedenceRelation* ; // /NextComponentToStart:Component/ ;
-Component ::= <Name:String> <DockerComposeName:String> <MqttTopicPrefix:String> <Status:String> [AutoSetStatus] <StartAsUp:boolean> /<NextCommand:String>/ ;
+Component ::= <Name:String> [StartStrategy] [ReportStrategy] <Status:String> [AutoSetStatus] <StartAsUp:boolean> /<NextCommand:String>/ ;
 rel Component.Predecessor* <-> Component.Successor* ;
 
+abstract StartStrategy ;
+DockerComposeStartStrategy : StartStrategy ::= <Name:String> ;
+ScriptStartStrategy : StartStrategy ::= <Command> <Cwd> ;
+ManualStartStrategy : StartStrategy ;
+
+abstract ReportStrategy ;
+MqttReportStrategy : ReportStrategy ::= <TopicPrefix:String> ;
+
 AutoSetStatus ::= <Status:String> <DelayInSeconds:int> ;
 
 ParsedPrecedenceRelation ;
diff --git a/src/main/java/de/tudresden/inf/st/coordinator/MainCoordinator.java b/src/main/java/de/tudresden/inf/st/coordinator/MainCoordinator.java
index 4683704fbfd1aa0fc228abc66cee39c34ad0560f..c9356642291dcca6ab319af7e8d21e094d93fdc6 100644
--- a/src/main/java/de/tudresden/inf/st/coordinator/MainCoordinator.java
+++ b/src/main/java/de/tudresden/inf/st/coordinator/MainCoordinator.java
@@ -85,8 +85,11 @@ public class MainCoordinator implements Callable<Integer> {
     Runtime.getRuntime().addShutdownHook(new Thread(this::close));
 
     for (Component comp : coordinator.getComponentList()) {
-      if (comp.getMqttTopicPrefix() != null && !comp.getMqttTopicPrefix().isBlank()) {
-        comp.connectStatus("mqtt://" + mqttHost + "/" + comp.getMqttTopicPrefix() + "/status");
+      if (comp.hasReportStrategy() && comp.getReportStrategy().isMqttReportStrategy()) {
+        MqttReportStrategy mqttStrategy = comp.getReportStrategy().asMqttReportStrategy();
+        if (mqttStrategy.getTopicPrefix() != null && !mqttStrategy.getTopicPrefix().isBlank()) {
+          comp.connectStatus("mqtt://" + mqttHost + "/" + mqttStrategy.getTopicPrefix() + "/status");
+        }
       }
       final String commandTopic;
       if (comp.getStartAsUp()) {
@@ -94,26 +97,35 @@ public class MainCoordinator implements Callable<Integer> {
         commandTopic = "coordinator/" + comp.getName();
         mainHandler.newConnection(commandTopic, bytes -> {
           try {
-            comp.callDockerCompose();
+            comp.getStartStrategy().start();
             comp.setStatus("ready");
           } catch (IOException | InterruptedException e) {
             comp.setStatus("failedStart");
             e.printStackTrace();
           }
         });
+      } else if (comp.hasReportStrategy() && comp.getReportStrategy().isMqttReportStrategy()) {
+        commandTopic = comp.getReportStrategy().asMqttReportStrategy().getTopicPrefix() + "/command";
       } else {
-        commandTopic = comp.getMqttTopicPrefix() + "/command";
+        commandTopic = null;
+      }
+      if (commandTopic != null) {
+        comp.connectNextCommand("mqtt://" + mqttHost + "/" + commandTopic, false);
       }
-      comp.connectNextCommand("mqtt://" + mqttHost + "/" + commandTopic, false);
     }
 
     Set<Component> alreadyRunning = coordinator.getRunningComponents();
     for (Component comp : coordinator.getComponentList()) {
+      if (alreadyRunning.contains(comp)) {
+        comp.setStatus("up");
+        if (comp.hasAutoSetStatus() && !comp.getStartAsUp()) {
+          schduleAutoSetStatus(comp);
+        }
+      }
       if (!alreadyRunning.contains(comp) && !comp.getStartAsUp()) {
-        comp.callDockerCompose();
+        comp.getStartStrategy().start();
         if (comp.hasAutoSetStatus()) {
-          executor.schedule(() -> comp.setStatus(comp.getAutoSetStatus().getStatus()),
-              comp.getAutoSetStatus().getDelayInSeconds(), TimeUnit.SECONDS);
+          schduleAutoSetStatus(comp);
         }
       }
     }
@@ -122,6 +134,13 @@ public class MainCoordinator implements Callable<Integer> {
     return 0;
   }
 
+  private void schduleAutoSetStatus(Component comp) {
+    executor.schedule(() -> {
+      System.out.println("Setting status of " + comp.getName() + " to " + comp.getAutoSetStatus().getStatus());
+      comp.setStatus(comp.getAutoSetStatus().getStatus());
+    }, comp.getAutoSetStatus().getDelayInSeconds(), TimeUnit.SECONDS);
+  }
+
   private void printStatus(String message) {
     String content = message.contains("detail") ? coordinator.details() : coordinator.prettyPrint();
     System.out.println(content);