diff --git a/bundle.md b/bundle.md
new file mode 100644
--- /dev/null
+++ b/bundle.md
@@ -0,0 +1,57 @@
+# Creating bundle from images
+
+## NAME
+
+Save set of images to the single archive.
+
+## SYNOPSIS
+
+```gradle
+plugins {
+    // task types and base extension
+    id "org.implab.gradle-container-base"
+}
+
+container {
+    cliCmd = "podman"
+}
+
+// create configuration
+configurations {
+    bundleImages {
+        canBeResolved = true
+        canBeConsumed = false
+    }
+}
+
+// add dependencies to the project
+dependencies {
+    bundleImages project(":images:foo"), project(":images:bar")
+}
+
+// create task to export bundle
+task bundle(type: SaveImage) {
+    // add image refs from artifacts
+    imageRefs configurations.bundleImages
+
+    // add image name
+    exportImages.add "nginx:latest"
+}
+```
+
+## DESCRIPTION
+
+To create an archive with images the task of type `SaveImage` can be used. This
+task has the following properties:
+
+| Property | Description |
+|-|-|
+| `archiveFileName` | The file name of the bundle archive, defaults to `{archiveBaseName}-{archiveVersion}-{archiveClassifier}.{archiveExtension}`.|
+| `archiveBaseName` | The base name of the archive, defaults to `{project.group}-{project.name}`. |
+| `archiveVersion` | The archive version, defaults to `{project.version}`. |
+| `exportImages` | A set of image names to include in the bundle. |
+
+| Method | Description |
+|-|-|
+| `imageRefs(FileCollection)` | Adds a set of files with image refs to add to the bundle. |
+| `imageRef(File)` | Adds an image name from the file with image reference. |
diff --git a/compose.md b/compose.md
new file mode 100644
--- /dev/null
+++ b/compose.md
@@ -0,0 +1,148 @@
+# Compose project
+
+## NAME
+
+`org.implab.gradle-container-compose` - docker compose project.
+
+## SYNOPSIS
+
+```gradle
+plugins {
+    id "org.implab.gradle-container-compose"
+}
+
+dependencies {
+    composeImages project(":images:foo") {
+        ext {
+            // imageName from :images:foo will be written to .env as FOO_IMAGE var
+            composeVar = "FOO_IMAGE"
+        }
+    }
+}
+
+writeEnv {
+    // write additional variables to .env
+    env "DEBUG_JAVA" put "yes"
+
+    // set compose name, this variable is set to that value by default
+    env "COMPOSE_PROJECT_NAME" put project.group
+}
+
+// base container extension
+container {
+    cliCmd = "podman"
+}
+
+// compose parameters
+compose {
+    // add compose profiles
+    profiles.add("dev")
+
+    // set compose file name
+    // defaults to compose.yaml
+    composeFileName = "docker-compose.yaml"
+}
+
+```
+
+## DESCRIPTION
+
+This plugin creates a set of conventional tasks to prepare and start compose
+project. This can be used to create and run sandbox.
+
+These tasks are:
+
+* `build` - prepares the context for the compose file, depends on `processResources`
+  and `writeEnv` tasks.
+* `up` - invokes `compose up -d` in the built context and starts up the project
+  in the background.
+* `stop` - invokes `compose stop` and stops the projects.
+* `rm` - invokes `compose rm` and removes containers, by default temporary volumes
+  are removed with containers.
+* `clean` - cleanups the build directory and removes built context.
+
+Special configuration `composeImages` should be used to add a dependencies to the
+images needed by this project. The dependencies must provide a single artifact
+a json file containing `tag` property. This configuration well be resolved and
+tag for each dependency will be stored in the environment variable specified with
+`composeVar` property.
+
+```gradle
+configuration {
+    composeImages file("nginx.json") {
+        ext {
+            composeVar = "NGINX_IMAGE"
+        }
+    }
+    composeImage project(":images:foo")  {
+        ext {
+            composeVar = "FOO_IMAGE"
+        }
+    }
+}
+```
+
+The compose environment variables are written to the `.env` file inside the
+context directory and will be used by `compose up` command.
+
+## Tasks
+
+### writeEnv
+
+`type: WriteEnv, dependsOn: [configurations.composeImages, processResources]`
+
+Inspects configuration `composeImages`, adds default `COMPOSE_PROJECT_NAME`
+variable and writes `.env` file to the context directory.
+
+This task provides a property `environment` of type `MapProperty<String, String>`
+which can be used to customize the compose environment.
+
+```gradle
+writeEnv {
+    // direct environment property access
+    environment.put("VAR_1", valueOrProvider)
+
+    // syntax sugar to modify environment property
+    env "VAR_2" put "value" // simple value
+    env "VAR_3" put provider { getSomeValue() } // provider
+
+    // map provider will be merged into the 
+    env {
+        VAR_4 = "val1"
+        VAR_5 = getAnotherValue()
+    }
+}
+```
+
+### processResources
+
+`type: Copy`
+
+Copies resources from the source directory `src/main` into the context directory `build/context`
+
+### up
+
+`type: ComposeUp, dependsOn: [buildTask]`
+
+Starts the compose project. The project is started in the background.
+
+### build
+
+`type: DefaultTask, dependsOn: [writeEnvTask]`
+
+This is a meta task used to group the set of tasks related to the build target.
+
+### stop
+
+`type: ComposeStop`
+
+Stops the current compose project.
+
+### rm
+
+`type: ComposeRm`
+
+Removes containers created from this compose project.
+
+`removeValues` - boolean property, if set to true the task will remove all
+temporary volumes left from containers.
diff --git a/container/src/main/java/org/implab/gradle/containers/ComposePlugin.java b/container/src/main/java/org/implab/gradle/containers/ComposePlugin.java
--- a/container/src/main/java/org/implab/gradle/containers/ComposePlugin.java
+++ b/container/src/main/java/org/implab/gradle/containers/ComposePlugin.java
@@ -3,6 +3,7 @@ package org.implab.gradle.containers;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 
 import org.gradle.api.DefaultTask;
 import org.gradle.api.Plugin;
@@ -21,6 +22,8 @@ import org.implab.gradle.containers.task
 public abstract class ComposePlugin implements Plugin<Project>, ProjectMixin {
     public final String COMPOSE_IMAGES_CONFIGURATION = "composeImages";
 
+    public final String COMPOSE_PROJECT_NAME = "COMPOSE_PROJECT_NAME";
+
     public final String COMPOSE_EXTENSION = "compose";
 
     public final String COMPOSE_UP_TASK = "up";
@@ -75,6 +78,11 @@ public abstract class ComposePlugin impl
             t.dependsOn(processResources, containerImages);
             t.getEnvFile().set(containerExtension.getContextDirectory().file(ENV_FILE_NAME));
 
+            var group = project.getGroup();
+            if (group != null && group.toString().length() > 0) {
+                t.getEnvironment().put(COMPOSE_PROJECT_NAME, group.toString());
+            }
+
             t.getEnvironment().putAll(containerImages.map(this::extractComposeEnv));
 
         });
diff --git a/container/src/main/java/org/implab/gradle/containers/cli/DockerTraits.java b/container/src/main/java/org/implab/gradle/containers/cli/DockerTraits.java
--- a/container/src/main/java/org/implab/gradle/containers/cli/DockerTraits.java
+++ b/container/src/main/java/org/implab/gradle/containers/cli/DockerTraits.java
@@ -106,7 +106,7 @@ public abstract class DockerTraits {
         complete(startProcess(builder(args)));
     }
 
-    public void saveImage(List<String> images, File output) throws InterruptedException, IOException {
+    public void saveImage(Set<String> images, File output) throws InterruptedException, IOException {
         if (output.exists())
             output.delete();
 
diff --git a/container/src/main/java/org/implab/gradle/containers/tasks/SaveImage.java b/container/src/main/java/org/implab/gradle/containers/tasks/SaveImage.java
--- a/container/src/main/java/org/implab/gradle/containers/tasks/SaveImage.java
+++ b/container/src/main/java/org/implab/gradle/containers/tasks/SaveImage.java
@@ -8,9 +8,9 @@ import java.util.Optional;
 import org.gradle.api.file.DirectoryProperty;
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.file.RegularFile;
-import org.gradle.api.provider.ListProperty;
 import org.gradle.api.provider.Property;
 import org.gradle.api.provider.Provider;
+import org.gradle.api.provider.SetProperty;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.Internal;
 import org.gradle.api.tasks.OutputFile;
@@ -21,7 +21,7 @@ import org.implab.gradle.containers.cli.
 public abstract class SaveImage extends DockerCliTask {
 
     @Input
-    public abstract ListProperty<String> getExportImages();
+    public abstract SetProperty<String> getExportImages();
 
     @OutputFile
     public Provider<RegularFile> getArchiveFile() {
diff --git a/container/src/main/java/org/implab/gradle/containers/tasks/TagImage.java b/container/src/main/java/org/implab/gradle/containers/tasks/TagImage.java
--- a/container/src/main/java/org/implab/gradle/containers/tasks/TagImage.java
+++ b/container/src/main/java/org/implab/gradle/containers/tasks/TagImage.java
@@ -1,7 +1,11 @@
 package org.implab.gradle.containers.tasks;
 
 import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.gradle.api.provider.Property;
+import org.gradle.api.provider.SetProperty;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.TaskAction;
 
@@ -10,12 +14,33 @@ public abstract class TagImage extends D
     public abstract Property<String> getSrcImage();
 
     @Input
+    public abstract SetProperty<String> getTags();
+
+    @Input
+    @Deprecated
     public abstract Property<String> getDestImage();
 
+    private Set<String> getImageTags() {
+        var tags = new HashSet<>(getTags().get());
+        tags.add(getDestImage().get());
+        return tags;
+    }
+
+    public TagImage() {
+        this.setOnlyIf("No tags were specified", self -> getImageTags().size() > 0);
+    }
+
     @TaskAction
     public void run() throws InterruptedException, IOException {
-        docker().tagImage(
-                getSrcImage().get(),
-                getDestImage().get());
+        var tags = getImageTags();
+        var src = getSrcImage().get();
+
+        if (tags.size() == 0)
+            getLogger().info("No tags were specified");
+
+        for (var tag : tags) {
+            getLogger().info("Tag: {}", tag);
+            docker().tagImage(src, tag);
+        }
     }
 }
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -5,7 +5,7 @@
 ```gradle
 
 plugins {
-    id 'org.implab.gradle-container' version '1.1'
+    id 'org.implab.gradle-container'
 }
 
 container {
@@ -44,58 +44,168 @@ task printVersion {
 ## Description
 
 This plugin is a simple wrapper around docker CLI. All the image
-building process is deligated to the `Dockerfile` which will run
-in the prepeared build context.
+building process is delegated to the `Dockerfile` which will run
+in the prepared build context.
 
-### Project structure
+## Project structure
 
-* `build/`
-    * `context/` - the build context where `docker build` command will run.
-    * `imageid` - the file storing the id of the image has been built.
-    * `image-name-1.2.3.tgz` - the exported image if `saveImage` has been executed.
+* `build/` - this folder will be created during build, it can be useful while
+  solving Dockerfile problems
+  * `context/` - the build context where `docker build` command will run.
+  * `imageid` - the file storing the id of the image has been built.
+  * `image-name-1.2.3.tgz` - the exported image if `saveImage` has been executed.
 * `src`
-    * `main` - the source files which will be copied to the build context.
+  * `main` - the source files which will be copied to the build context.
+
+## Global properties
 
-## Properties
+There are several global properties recognized by this plugin in the project.
+These properties affect images naming and publication and they are useful in
+multi-project environment.
 
 `imagesAuthority` - the registry where the image should be published.
 for example `docker.io`
 
 `imagesGroup` - the path to the image in the repository.
 
+`containerCli` - the command line cli, this property corresponds to
+`container.cliCmd` in the project.
+
+Properties defined in the project takes precedence over global properties.
+
+## Image names
+
+```gradle
+plugins {
+    id "org.implab.gradle-container"
+}
+
+container {
+    // image authority, the repository for your images
+    // defaults to global imagesAuthority property or none
+    imageAuthority = "my.registry.org"
+
+    // the image path 
+    // defaults to global imagesGroup property or none
+    imageGroup = "my/project"
+
+    // the name of the image
+    // defaults to project.name
+    imageLocalName = "foo"
+}
+
+// provider for imageName, returns ImageName object
+// ImageName consists of "{imageAuthority}/{imageGroup}/{imageLocalName}"
+def imageNameProvider = container.imageName
+```
+
 ## Tasks
 
+Some tasks support passing additional options as additional command line
+parameters. These task has the property `options` and some additional methods.
+
+| Property | Description |
+|--|--|
+| `options` | A list of additional arguments passed to `docker build` command. |
+
+| Method | Description |
+|---|---|
+| `option(String)` | Adds option to `options`. |
+| `option(Closure)` | Converts the parameter to provider and adds it to `options`. |
+| `option(Callable)` | Converts the parameter to provider and adds it to `options`. |
+| `options(String...)` | Adds specified options to `options`. |
+| `options(Closure)`| Converts the parameter to provider and adds it to `options`. |
+| `options(Callable)`| Converts the parameter to provider and adds it to `options` |
+
 ### buildImage
 
+`type: BuildImage`
+
 The task builds the image. Wrapper around the `docker build` command.
 
+| Property | Description |
+|---|---|
+| `contextDirectory` | A Dockerfile context directory. Set to `container.context`. |
+| `buildArgs` | A dictionary with environment variables which are set during build. |
+| `buildTarget` | A target image for the multi-stage builds. Defaults to none. |
+| `imageName` | A name (tag) for the resulting image. |
+| `imageIdFIle` | Output file name where image ref will be written. |
+
+This task also supports additional command line options.
+
 ### saveImage
 
+`type: SaveImage`
+
 The task exports image as the .tar archive.
 
+| Property | Description |
+|-|-|
+| `archiveFileName` | The file name of the bundle archive, defaults to `{archiveBaseName}-{archiveVersion}-{archiveClassifier}.{archiveExtension}`.|
+| `archiveBaseName` | The base name of the archive, defaults to `{project.group}-{project.name}`. |
+| `archiveVersion` | The archive version, defaults to `{project.version}`. |
+| `exportImages` | A set of image names to include in the bundle. |
+
+| Method | Description |
+|-|-|
+| `imageRefs(FileCollection)` | Adds a set of files with image refs to add to the bundle. |
+| `imageRef(File)` | Adds an image name from the file with image reference. |
+
 ### pushImage
 
-The task pushes the image to the remote repository.
+The task pushes the image to the remote repository (imageAuthority).
+
+[Since v1.2] This task also supports additional command line options. You can use them to
+push all tags for the image.
+
+```gradle
+pushImage {
+    option "--all-tags"
+}
+```
 
 ### processResources
 
-The copy task, it prepares the build context. Use it to customize
+The copy task, it prepares the build context. Use this task to customize
 the build context.
 
 ### tagImage
 
+`type: TagImage`
+
 since: 1.1
 
 ```gradle
 task tagLatest(type: TagImage) {
+    pushImage.dependsOn it
+
     srcImage = container.imageName
-    destImage = container.imageName.map { it.tag("latest") }
+
+    tags.add(container.imageName.map { it.tag("latest") })
 }
 
 ```
 
+| Property | Description |
+|-|-|
+| `srcImage` | The source image name to add tag to. |
+| `tags` | The set of tags to add to the image. |
+
+## See also
+
+* Creating [compose](compose.md) project
+* Creating [bundle](bundle.md) project
+
 ## Changes
 
+### 1.2
+
+Added `org.implab.gradle-container-base`, `org.implab.gradle-container-compose`
+plugins.
+
+* `org.implab.gradle-container-base` registers base extension and task types.
+* `org.implab.gradle-container-compose` registers conventional tasks.
+
 ### 1.1
 
 Warning! This version isn't fully backward compatible with 1.0 version.