diff --git a/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf b/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf
new file mode 100644
index 0000000000..70161f6946
--- /dev/null
+++ b/example/C/src/DistributedHelloWorld/docker/HelloWorldContainerized.lf
@@ -0,0 +1,22 @@
+/**
+ * Containerized distributed LF program where a MessageGenerator creates a string
+ * message that is sent via the RTI (runtime infrastructure) to a
+ * receiver that prints the message.
+ * 
+ * For run instructions, see README.
+ * 
+ * @author Edward A. Lee
+ */
+target C {
+    timeout: 10 secs,
+    docker: true
+};
+
+import MessageGenerator from "../HelloWorld.lf"
+import PrintMessage from "../HelloWorld.lf"
+
+federated reactor HelloWorldContainerized at rti {
+    source = new MessageGenerator(prefix = "Hello World");
+    print = new PrintMessage();
+    source.message -> print.message;
+}
\ No newline at end of file
diff --git a/example/C/src/DistributedHelloWorld/docker/README.md b/example/C/src/DistributedHelloWorld/docker/README.md
new file mode 100644
index 0000000000..0d9bb582ca
--- /dev/null
+++ b/example/C/src/DistributedHelloWorld/docker/README.md
@@ -0,0 +1,51 @@
+# ContainerizedFederatedHelloWorld
+This is a basic example showing containerized federated execution. 
+
+For more details, see: [Containerized Execution in Lingua Franca](https://github.com/lf-lang/lingua-franca/wiki/Containerized-Execution)
+
+## Run instructions
+Put HelloWorld.lf in a src directory.
+Then, run:
+```bash
+lfc HelloWorldContainerized.lf
+```
+
+There would be 3 build messages, 1 for the RTI and 2 for the reactors, indicating where the docker file is generated, as well as the instruction to build the docker file. 
+
+A message would look something like this: 
+```
+Dockerfile for <something> written to <some_directory>/<name_of_dockerfile>
+#####################################
+To build the docker image, use:
+   
+    docker build -t <some_name> -f <some_directory>/<name_of_dockerfile> <some_path>
+
+#####################################
+```
+
+If you cannot find the build message for the RTI, try a clean build using:
+```bash
+lfc --clean HelloWorld.lf
+```
+
+Then, use the printed commands to build the 3 docker images. 
+
+Set up a docker network using 
+```bash
+docker network create lf
+```
+
+Open 3 terminals, 1 for the RTI and 1 for each reactor.
+
+Run the RTI:
+```bash
+docker run --rm -it --network=lf --name=rti rti -i 1 -n 2
+```
+
+Run the two reactors:
+```bash
+docker run --rm -it --network=lf helloworldcontainerized_source -i 1
+```
+```bash
+docker run --rm -it --network=lf helloworldcontainerized_print -i 1
+```
\ No newline at end of file
diff --git a/org.lflang/src/org/lflang/TargetProperty.java b/org.lflang/src/org/lflang/TargetProperty.java
index 311c67ff53..38222d8f17 100644
--- a/org.lflang/src/org/lflang/TargetProperty.java
+++ b/org.lflang/src/org/lflang/TargetProperty.java
@@ -167,7 +167,7 @@ public enum TargetProperty {
      * true or false, or a dictionary of options.
      */
     DOCKER("docker", UnionType.DOCKER_UNION,
-            Arrays.asList(Target.C, Target.CCPP), (config, value, err) -> {
+            Arrays.asList(Target.C, Target.CCPP, Target.Python), (config, value, err) -> {
                 if (value.getLiteral() != null) {
                     if (ASTUtils.toBoolean(value)) {
                         config.dockerOptions = new DockerOptions();
diff --git a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
index 12b53ec62c..2801003246 100644
--- a/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
+++ b/org.lflang/src/org/lflang/generator/GeneratorBase.xtend
@@ -952,6 +952,34 @@ abstract class GeneratorBase extends AbstractLFValidator implements TargetTypes
         return false
     }
 
+    /**
+     * Copy the core files needed to build the RTI within a container.
+     *
+     * @param the directory where rti.Dockerfile is located.
+     * @param the core files used for code generation in the current target.
+     */
+    def copyRtiFiles(File rtiDir, ArrayList<String> coreFiles) {
+        var rtiFiles = newArrayList()
+        rtiFiles.addAll(coreFiles)
+
+        // add the RTI files on top of the coreFiles
+        rtiFiles.addAll(
+            "federated/RTI/rti.h",
+            "federated/RTI/rti.c",
+            "federated/RTI/CMakeLists.txt"
+        )
+        fileConfig.copyFilesFromClassPath("/lib/c/reactor-c/core", rtiDir + File.separator + "core", rtiFiles)
+    }
+
+    /**
+     * Write a Dockerfile for the current federate as given by filename.
+     * @param the name given to the docker file (without any extension).
+     */
+    def writeDockerFile(String dockerFileName) {
+        throw new UnsupportedOperationException("This target does not support docker file generation.")
+    }
+    
+
     /**
      * Parsed error message from a compiler is returned here.
      */
diff --git a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend
index 8e2473dbc5..fdcc33644d 100644
--- a/org.lflang/src/org/lflang/generator/PythonGenerator.xtend
+++ b/org.lflang/src/org/lflang/generator/PythonGenerator.xtend
@@ -1059,7 +1059,7 @@ class PythonGenerator extends CGenerator {
         targetConfig.noCompile = compileStatus
 
         if (errorsOccurred) return;
-
+        
         var baseFileName = topLevelName
         for (federate : federates) {
             if (isFederated) {
@@ -1092,7 +1092,6 @@ class PythonGenerator extends CGenerator {
                     printRunInfo();
                 }
             }
-
         }
         // Restore filename
         topLevelName = baseFileName
@@ -1699,6 +1698,50 @@ class PythonGenerator extends CGenerator {
         }
     }
     
+    /**
+     * Write a Dockerfile for the current federate as given by filename.
+     * The file will go into src-gen/filename.Dockerfile.
+     * If there is no main reactor, then no Dockerfile will be generated
+     * (it wouldn't be very useful).
+     * @param The root filename (without any extension).
+     */
+    override writeDockerFile(String filename) {
+        var srcGenPath = fileConfig.getSrcGenPath
+        val dockerFile = srcGenPath + File.separator + filename + '.Dockerfile'
+        // If a dockerfile exists, remove it.
+        var file = new File(dockerFile)
+        if (file.exists) {
+            file.delete
+        }
+
+        if (this.mainDef === null) {
+            return
+        }
+
+        val contents = new StringBuilder()
+        pr(contents, '''
+            # Generated docker file for «topLevelName».lf in «srcGenPath».
+            # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution
+            FROM python:alpine
+            WORKDIR /lingua-franca/«topLevelName»
+            COPY . src-gen
+            RUN set -ex && apk add --no-cache gcc musl-dev \
+             && cd src-gen && python3 setup.py install && cd .. \
+             && apk del gcc musl-dev
+            ENTRYPOINT ["python3", "src-gen/«filename».py"]
+        ''')
+        writeSourceCodeToFile(contents.toString.getBytes, dockerFile)
+        println("Dockerfile written to " + dockerFile)
+        println('''
+            #####################################
+            To build the docker image, use:
+               
+                docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «srcGenPath»
+            
+            #####################################
+        ''')
+    }
+    
     /**
      * Convert C types to formats used in Py_BuildValue and PyArg_PurseTuple.
      * This is unused but will be useful to enable inter-compatibility between 
diff --git a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
index cfee8c2b07..be5a948fc8 100644
--- a/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
+++ b/org.lflang/src/org/lflang/generator/c/CGenerator.xtend
@@ -27,6 +27,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 package org.lflang.generator.c
 
 import java.io.File
+import java.nio.file.Path
 import java.util.ArrayList
 import java.util.Collection
 import java.util.LinkedHashMap
@@ -537,6 +538,14 @@ class CGenerator extends GeneratorBase {
                 "federated/clock-sync.c"
             );
             createFederatedLauncher(coreFiles);
+
+            var rtiPath = fileConfig.getSrcGenBasePath().resolve("RTI")
+            var rtiDir = rtiPath.toFile()
+            if (!rtiDir.exists()) {
+                rtiDir.mkdirs()
+            }
+            writeRTIDockerFile(rtiPath, rtiDir)
+            copyRtiFiles(rtiDir, coreFiles)
         }
 
         // Perform distinct code generation into distinct files for each federate.
@@ -1162,13 +1171,6 @@ class CGenerator extends GeneratorBase {
         // for more detail.
         if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) {
             // Mac support
-            coreFiles.add("platform/lf_POSIX_threads_support.c")
-            coreFiles.add("platform/lf_C11_threads_support.c")
-            coreFiles.add("platform/lf_POSIX_threads_support.h")
-            coreFiles.add("platform/lf_C11_threads_support.h")
-            coreFiles.add("platform/lf_macos_support.c")            
-            coreFiles.add("platform/lf_macos_support.h")
-            coreFiles.add("platform/lf_unix_clock_support.c")
             // If there is no main reactor, then compilation will produce a .o file requiring further linking.
             // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file
             // will detect and use the appropriate platform file based on the platform that cmake is invoked on.
@@ -1179,12 +1181,6 @@ class CGenerator extends GeneratorBase {
             }
         } else if (OS.indexOf("win") >= 0) {
             // Windows support
-            coreFiles.add("platform/lf_C11_threads_support.c")
-            coreFiles.add("platform/lf_C11_threads_support.h")
-            coreFiles.add("platform/lf_windows_support.c")
-            coreFiles.add("platform/lf_windows_support.h")
-            // For 64-bit epoch time
-            coreFiles.add("platform/lf_unix_clock_support.c")
             // If there is no main reactor, then compilation will produce a .o file requiring further linking.
             // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file
             // will detect and use the appropriate platform file based on the platform that cmake is invoked on.
@@ -1195,13 +1191,6 @@ class CGenerator extends GeneratorBase {
             }
         } else if (OS.indexOf("nux") >= 0) {
             // Linux support
-            coreFiles.add("platform/lf_POSIX_threads_support.c")
-            coreFiles.add("platform/lf_C11_threads_support.c")
-            coreFiles.add("platform/lf_POSIX_threads_support.h")
-            coreFiles.add("platform/lf_C11_threads_support.h")
-            coreFiles.add("platform/lf_linux_support.c")
-            coreFiles.add("platform/lf_linux_support.h")
-            coreFiles.add("platform/lf_unix_clock_support.c")
             // If there is no main reactor, then compilation will produce a .o file requiring further linking.
             // Also, if useCmake is set to true, we don't need to add platform files. The CMakeLists.txt file
             // will detect and use the appropriate platform file based on the platform that cmake is invoked on.
@@ -1213,6 +1202,21 @@ class CGenerator extends GeneratorBase {
         } else {
             errorReporter.reportError("Platform " + OS + " is not supported")
         }
+
+        coreFiles.addAll(
+             "platform/lf_POSIX_threads_support.c",
+             "platform/lf_C11_threads_support.c",
+             "platform/lf_C11_threads_support.h",
+             "platform/lf_POSIX_threads_support.h",
+             "platform/lf_POSIX_threads_support.c",
+             "platform/lf_unix_clock_support.c",
+             "platform/lf_macos_support.c",
+             "platform/lf_macos_support.h",
+             "platform/lf_windows_support.c",
+             "platform/lf_windows_support.h",
+             "platform/lf_linux_support.c",
+             "platform/lf_linux_support.h"
+         )
     }
     
     /**
@@ -1239,24 +1243,27 @@ class CGenerator extends GeneratorBase {
      * The file will go into src-gen/filename.Dockerfile.
      * If there is no main reactor, then no Dockerfile will be generated
      * (it wouldn't be very useful).
-     * @param The root filename (without any extension).
+     * @param the name given to the docker file (without any extension).
      */
-    def writeDockerFile(String filename) {
-        if (this.mainDef === null) {
-            return
-        }
-        
+    override writeDockerFile(String dockerFileName) {
         var srcGenPath = fileConfig.getSrcGenPath
-        val dockerFile = srcGenPath + File.separator + filename + '.Dockerfile'
-        val contents = new StringBuilder()
-        
+        val dockerFile = srcGenPath + File.separator + dockerFileName + '.Dockerfile'
         // If a dockerfile exists, remove it.
         var file = new File(dockerFile)
         if (file.exists) {
             file.delete
         }
-        // The Docker configuration uses gcc, so config.compiler is ignored here.
-        var compileCommand = '''gcc «targetConfig.compilerFlags.join(" ")» src-gen/«filename».c -o bin/«filename»'''
+        if (this.mainDef === null) {
+            return
+        }
+        
+        val contents = new StringBuilder()
+        // The Docker configuration uses cmake, so config.compiler is ignored here.
+        var compileCommand = '''
+        cmake -S src-gen -B bin && \
+        cd bin && \
+        make all
+        '''
         if (!targetConfig.buildCommands.nullOrEmpty) {
             compileCommand = targetConfig.buildCommands.join(' ')
         }
@@ -1264,27 +1271,96 @@ class CGenerator extends GeneratorBase {
         if (!targetConfig.fileNames.nullOrEmpty) {
             additionalFiles = '''COPY "«targetConfig.fileNames.join('" "')»" "src-gen/"'''
         }
+        var dockerCompiler = 'gcc'
+        var fileExtension = 'c'
+
+        if (CCppMode) {
+            dockerCompiler = 'g++'
+            fileExtension = 'cpp'
+        }
+
         pr(contents, '''
-            # Generated docker file for «topLevelName».lf in «srcGenPath».
+            # Generated docker file for «topLevelName» in «srcGenPath».
             # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution
-            FROM «targetConfig.dockerOptions.from»
-            WORKDIR /lingua-franca
-            COPY src-gen/core src-gen/core
-            COPY "src-gen/«filename».c" "src-gen/ctarget.h" "src-gen/"
+            FROM «targetConfig.dockerOptions.from» AS builder
+            WORKDIR /lingua-franca/«topLevelName»
+            RUN set -ex && apk add --no-cache «dockerCompiler» musl-dev cmake make
+            COPY core src-gen/core
+            COPY ctarget.h ctarget.c src-gen/
+            COPY CMakeLists.txt \
+                 «topLevelName».«fileExtension» src-gen/
             «additionalFiles»
             RUN set -ex && \
-                apk add --no-cache gcc musl-dev && \
                 mkdir bin && \
-                «compileCommand» && \
-                apk del gcc musl-dev && \
-                rm -rf src-gen
+                «compileCommand»
+            
+            FROM «targetConfig.dockerOptions.from» 
+            WORKDIR /lingua-franca
+            RUN mkdir bin
+            COPY --from=builder /lingua-franca/«topLevelName»/bin/«topLevelName» ./bin/«topLevelName»
+            
             # Use ENTRYPOINT not CMD so that command-line arguments go through
-            ENTRYPOINT ["./bin/«filename»"]
+            ENTRYPOINT ["./bin/«topLevelName»"]
         ''')
         writeSourceCodeToFile(contents.toString.getBytes, dockerFile)
-        println("Dockerfile written to " + dockerFile)
+        println('''Dockerfile for «topLevelName» written to ''' + dockerFile)
+        println('''
+            #####################################
+            To build the docker image, use:
+               
+                docker build -t «topLevelName.toLowerCase()» -f «dockerFile» «srcGenPath»
+            
+            #####################################
+        ''')
     }
-    
+
+    /**
+     * Write a Dockerfile for the RTI at rtiDir.
+     * The file will go into src-gen/RTI/rti.Dockerfile.
+     * @param the directory where rti.Dockerfile will be written to.
+     */
+    def writeRTIDockerFile(Path rtiPath, File rtiDir) {
+        val dockerFileName = 'rti.Dockerfile'
+        val dockerFile = rtiDir + File.separator + dockerFileName
+        var srcGenPath = fileConfig.getSrcGenPath
+        // If a dockerfile exists, remove it.
+        var file = new File(dockerFile)
+        if (file.exists) {
+            file.delete
+        }
+        if (this.mainDef === null) {
+            return
+        }
+        val contents = new StringBuilder()
+        pr(contents, '''
+            # Generated docker file for RTI in «rtiDir».
+            # For instructions, see: https://github.com/icyphy/lingua-franca/wiki/Containerized-Execution
+            FROM alpine:latest
+            WORKDIR /lingua-franca/RTI
+            COPY core core
+            WORKDIR core/federated/RTI
+            RUN set -ex && apk add --no-cache gcc musl-dev cmake make && \
+                mkdir build && \
+                cd build && \
+                cmake ../ && \
+                make && \
+                make install
+
+            # Use ENTRYPOINT not CMD so that command-line arguments go through
+            ENTRYPOINT ["./build/RTI"]
+        ''')
+        writeSourceCodeToFile(contents.toString.getBytes, dockerFile)
+        println("Dockerfile for RTI written to " + dockerFile)
+        println('''
+            #####################################
+            To build the docker image, use:
+               
+                docker build -t rti -f «dockerFile» «rtiDir»
+            
+            #####################################
+        ''')
+    }
+
     /**
      * Initialize clock synchronization (if enabled) and its related options for a given federate.
      * 
@@ -1438,7 +1514,7 @@ class CGenerator extends GeneratorBase {
                     lf_thread_create(&_fed.inbound_p2p_handling_thread_id, handle_p2p_connections_from_federates, NULL);
                 ''')
             }
-                            
+
             for (remoteFederate : federate.outboundP2PConnections) {
                 pr('''connect_to_federate(«remoteFederate.id»);''')
             }