diff --git a/pkg/logentry/pipeline.go b/pkg/logentry/pipeline.go
index afac87bdff7cde8569ca659bbb2e75e659ad8b7a..c58f72c716e34bce68cca5b48eeb8417648c88fd 100644
--- a/pkg/logentry/pipeline.go
+++ b/pkg/logentry/pipeline.go
@@ -24,7 +24,8 @@ func NewPipeline(log log.Logger, stgs PipelineStages) (*Pipeline, error) {
 	for _, s := range stgs {
 		stage, ok := s.(map[interface{}]interface{})
 		if !ok {
-			return nil, errors.New("invalid YAML config")
+			return nil, errors.Errorf("invalid YAML config, "+
+				"make sure each stage of your pipeline is a YAML object (must end with a `:`), check stage `- %s`", s)
 		}
 		if len(stage) > 1 {
 			return nil, errors.New("pipeline stage must contain only one key")
@@ -47,6 +48,18 @@ func NewPipeline(log log.Logger, stgs PipelineStages) (*Pipeline, error) {
 					return nil, errors.Wrap(err, "invalid regex stage config")
 				}
 				st = append(st, regex)
+			case "docker":
+				docker, err := stages.NewDocker(log)
+				if err != nil {
+					return nil, errors.Wrap(err, "invalid docker stage config")
+				}
+				st = append(st, docker)
+			case "cri":
+				cri, err := stages.NewCri(log)
+				if err != nil {
+					return nil, errors.Wrap(err, "invalid cri stage config")
+				}
+				st = append(st, cri)
 			}
 		}
 	}
@@ -71,3 +84,13 @@ func (p *Pipeline) Wrap(next api.EntryHandler) api.EntryHandler {
 		return next.Handle(labels, timestamp, line)
 	})
 }
+
+// AddStage adds a stage to the pipeline
+func (p *Pipeline) AddStage(stage stages.Stage) {
+	p.stages = append(p.stages, stage)
+}
+
+// Size gets the current number of stages in the pipeline
+func (p *Pipeline) Size() int {
+	return len(p.stages)
+}
diff --git a/pkg/logentry/pipeline_test.go b/pkg/logentry/pipeline_test.go
index 75c529f0dae6c06762cb4e5140fed4a3a49d6086..07e047b78df156a4cac650c696f3f88a313c2f65 100644
--- a/pkg/logentry/pipeline_test.go
+++ b/pkg/logentry/pipeline_test.go
@@ -2,27 +2,31 @@ package logentry
 
 import (
 	"testing"
+	"time"
 
 	"github.com/cortexproject/cortex/pkg/util"
+	"github.com/prometheus/common/model"
+	"github.com/stretchr/testify/assert"
 
 	"gopkg.in/yaml.v2"
 )
 
+var rawTestLine = `{"log":"11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] \"GET /1986.js HTTP/1.1\" 200 932 \"-\" \"Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6\"","stream":"stderr","time":"2019-04-30T02:12:41.8443515Z"}`
+var processedTestLine = `11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] "GET /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"`
+
 var testYaml = `
 pipeline_stages:
+- docker:
 - regex:
-    expression: "./*"
-    labels:
-      stream:
-- json: 
+    expression: "^(?P<ip>\\S+) (?P<identd>\\S+) (?P<user>\\S+) \\[(?P<timestamp>[\\w:/]+\\s[+\\-]\\d{4})\\] \"(?P<action>\\S+)\\s?(?P<path>\\S+)?\\s?(?P<protocol>\\S+)?\" (?P<status>\\d{3}|-) (?P<size>\\d+|-)\\s?\"?(?P<referer>[^\"]*)\"?\\s?\"?(?P<useragent>[^\"]*)?\"?$"
     timestamp:
-      source: time
-      format: RFC3339
+      source: timestamp
+      format: "02/Jan/2006:15:04:05 -0700"
     labels:
-      stream:
-        source: json_key_name.json_sub_key_name
-    output:
-      source: log
+      action:
+        source: "action"
+      status_code:
+        source: "status"
 `
 
 func TestNewPipeline(t *testing.T) {
@@ -39,3 +43,60 @@ func TestNewPipeline(t *testing.T) {
 		t.Fatal("missing stages")
 	}
 }
+
+func TestPipeline_MultiStage(t *testing.T) {
+	est, err := time.LoadLocation("America/New_York")
+	if err != nil {
+		t.Fatal("could not parse timestamp", err)
+	}
+
+	var config map[string]interface{}
+	err = yaml.Unmarshal([]byte(testYaml), &config)
+	if err != nil {
+		panic(err)
+	}
+	p, err := NewPipeline(util.Logger, config["pipeline_stages"].([]interface{}))
+	if err != nil {
+		panic(err)
+	}
+
+	tests := map[string]struct {
+		entry          string
+		expectedEntry  string
+		t              time.Time
+		expectedT      time.Time
+		labels         model.LabelSet
+		expectedLabels model.LabelSet
+	}{
+		"happy path": {
+			rawTestLine,
+			processedTestLine,
+			time.Now(),
+			time.Date(2000, 01, 25, 14, 00, 01, 0, est),
+			map[model.LabelName]model.LabelValue{
+				"test": "test",
+			},
+			map[model.LabelName]model.LabelValue{
+				"test":        "test",
+				"stream":      "stderr",
+				"action":      "GET",
+				"status_code": "200",
+			},
+		},
+	}
+
+	for tName, tt := range tests {
+		tt := tt
+		t.Run(tName, func(t *testing.T) {
+			t.Parallel()
+
+			p.Process(tt.labels, &tt.t, &tt.entry)
+
+			assert.Equal(t, tt.expectedLabels, tt.labels, "did not get expected labels")
+			assert.Equal(t, tt.expectedEntry, tt.entry, "did not receive expected log entry")
+			if tt.t.Unix() != tt.expectedT.Unix() {
+				t.Fatalf("mismatch ts want: %s got:%s", tt.expectedT, tt.t)
+			}
+		})
+	}
+}
diff --git a/pkg/logentry/stages/extensions.go b/pkg/logentry/stages/extensions.go
new file mode 100644
index 0000000000000000000000000000000000000000..184457a1441ff07c8c3dd48745d54ad00fca29f2
--- /dev/null
+++ b/pkg/logentry/stages/extensions.go
@@ -0,0 +1,44 @@
+package stages
+
+import (
+	"github.com/go-kit/kit/log"
+)
+
+// NewDocker creates a Docker json log format specific pipeline stage.
+func NewDocker(logger log.Logger) (Stage, error) {
+	config := map[string]interface{}{
+		"timestamp": map[string]interface{}{
+			"source": "time",
+			"format": "RFC3339",
+		},
+		"labels": map[string]interface{}{
+			"stream": map[string]interface{}{
+				"source": "stream",
+			},
+		},
+		"output": map[string]interface{}{
+			"source": "log",
+		},
+	}
+	return NewJSON(logger, config)
+}
+
+// NewCri creates a CRI format specific pipeline stage
+func NewCri(logger log.Logger) (Stage, error) {
+	config := map[string]interface{}{
+		"expression": "^(?s)(?P<time>\\S+?) (?P<stream>stdout|stderr) (?P<flags>\\S+?) (?P<content>.*)$",
+		"timestamp": map[string]interface{}{
+			"source": "time",
+			"format": "RFC3339Nano",
+		},
+		"labels": map[string]interface{}{
+			"stream": map[string]interface{}{
+				"source": "stream",
+			},
+		},
+		"output": map[string]interface{}{
+			"source": "content",
+		},
+	}
+	return NewRegex(logger, config)
+}
diff --git a/pkg/logentry/stages/extensions_test.go b/pkg/logentry/stages/extensions_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4781d3399f833345c05c1910e5c84c0d1733d9ce
--- /dev/null
+++ b/pkg/logentry/stages/extensions_test.go
@@ -0,0 +1,157 @@
+package stages
+
+import (
+	"testing"
+	"time"
+
+	"github.com/cortexproject/cortex/pkg/util"
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	dockerRaw       = `{"log":"level=info ts=2019-04-30T02:12:41.844179Z caller=filetargetmanager.go:180 msg=\"Adding target\" key=\"{com_docker_deploy_namespace=\\\"docker\\\", com_docker_fry=\\\"compose.api\\\", com_docker_image_tag=\\\"v0.4.12\\\", container_name=\\\"compose\\\", instance=\\\"compose-api-cbff6dfc9-cqfr8\\\", job=\\\"docker/compose-api\\\", namespace=\\\"docker\\\", pod_template_hash=\\\"769928975\\\"}\"\n","stream":"stderr","time":"2019-04-30T02:12:41.8443515Z"}`
+	dockerProcessed = `level=info ts=2019-04-30T02:12:41.844179Z caller=filetargetmanager.go:180 msg="Adding target" key="{com_docker_deploy_namespace=\"docker\", com_docker_fry=\"compose.api\", com_docker_image_tag=\"v0.4.12\", container_name=\"compose\", instance=\"compose-api-cbff6dfc9-cqfr8\", job=\"docker/compose-api\", namespace=\"docker\", pod_template_hash=\"769928975\"}"
+`
+	dockerInvalidTimestampRaw = `{"log":"log message\n","stream":"stderr","time":"hi!"}`
+	dockerTestTimeNow         = time.Now()
+)
+
+func TestNewDocker(t *testing.T) {
+	loc, err := time.LoadLocation("UTC")
+	if err != nil {
+		t.Fatal("could not parse timezone", err)
+	}
+
+	tests := map[string]struct {
+		entry          string
+		expectedEntry  string
+		t              time.Time
+		expectedT      time.Time
+		labels         map[string]string
+		expectedLabels map[string]string
+	}{
+		"happy path": {
+			dockerRaw,
+			dockerProcessed,
+			time.Now(),
+			time.Date(2019, 4, 30, 02, 12, 41, 844351500, loc),
+			map[string]string{},
+			map[string]string{
+				"stream": "stderr",
+			},
+		},
+		"invalid timestamp": {
+			dockerInvalidTimestampRaw,
+			"log message\n",
+			dockerTestTimeNow,
+			dockerTestTimeNow,
+			map[string]string{},
+			map[string]string{
+				"stream": "stderr",
+			},
+		},
+		"invalid json": {
+			"i'm not json!",
+			"i'm not json!",
+			dockerTestTimeNow,
+			dockerTestTimeNow,
+			map[string]string{},
+			map[string]string{},
+		},
+	}
+
+	for tName, tt := range tests {
+		tt := tt
+		t.Run(tName, func(t *testing.T) {
+			t.Parallel()
+			p, err := NewDocker(util.Logger)
+			if err != nil {
+				t.Fatalf("failed to create Docker parser: %s", err)
+			}
+			lbs := toLabelSet(tt.labels)
+			p.Process(lbs, &tt.t, &tt.entry)
+
+			assertLabels(t, tt.expectedLabels, lbs)
+			assert.Equal(t, tt.expectedEntry, tt.entry, "did not receive expected log entry")
+			if tt.t.Unix() != tt.expectedT.Unix() {
+				t.Fatalf("mismatch ts want: %s got:%s", tt.expectedT, tt.t)
+			}
+		})
+	}
+}
+
+var (
+	criTestTimeStr = "2019-01-01T01:00:00.000000001Z"
+	criTestTime, _ = time.Parse(time.RFC3339Nano, criTestTimeStr)
+	criTestTime2   = time.Now()
+)
+
+func TestNewCri(t *testing.T) {
+	tests := map[string]struct {
+		entry          string
+		expectedEntry  string
+		t              time.Time
+		expectedT      time.Time
+		labels         map[string]string
+		expectedLabels map[string]string
+	}{
+		"happy path": {
+			criTestTimeStr + " stderr P message",
+			"message",
+			time.Now(),
+			criTestTime,
+			map[string]string{},
+			map[string]string{
+				"stream": "stderr",
+			},
+		},
+		"multi line pass": {
+			criTestTimeStr + " stderr P message\nmessage2",
+			"message\nmessage2",
+			time.Now(),
+			criTestTime,
+			map[string]string{},
+			map[string]string{
+				"stream": "stderr",
+			},
+		},
+		"invalid timestamp": {
+			"3242 stderr P message",
+			"message",
+			criTestTime2,
+			criTestTime2,
+			map[string]string{},
+			map[string]string{
+				"stream": "stderr",
+			},
+		},
+		"invalid line": {
+			"i'm invalid!!!",
+			"i'm invalid!!!",
+			criTestTime2,
+			criTestTime2,
+			map[string]string{},
+			map[string]string{},
+		},
+	}
+
+	for tName, tt := range tests {
+		tt := tt
+		t.Run(tName, func(t *testing.T) {
+			t.Parallel()
+			p, err := NewCri(util.Logger)
+			if err != nil {
+				t.Fatalf("failed to create CRI parser: %s", err)
+			}
+			lbs := toLabelSet(tt.labels)
+			p.Process(lbs, &tt.t, &tt.entry)
+
+			assertLabels(t, tt.expectedLabels, lbs)
+			assert.Equal(t, tt.expectedEntry, tt.entry, "did not receive expected log entry")
+			if tt.t.Unix() != tt.expectedT.Unix() {
+				t.Fatalf("mismatch ts want: %s got:%s", tt.expectedT, tt.t)
+			}
+		})
+	}
+
+}
diff --git a/pkg/logentry/stages/json.go b/pkg/logentry/stages/json.go
index 2ccb5a1337090980fda979f08db13499b92b1ba6..17e325e21ab81ef72ba28b6f5d77830ef5253dbb 100644
--- a/pkg/logentry/stages/json.go
+++ b/pkg/logentry/stages/json.go
@@ -153,6 +153,7 @@ func (j *jsonStage) getJSONValue(expr *string, data map[string]interface{}) (res
 func (j *jsonStage) Process(labels model.LabelSet, t *time.Time, entry *string) {
 	if entry == nil {
 		level.Debug(j.logger).Log("msg", "cannot parse a nil entry")
+		return
 	}
 	var data map[string]interface{}
 	if err := json.Unmarshal([]byte(*entry), &data); err != nil {
diff --git a/pkg/logentry/stages/json_test.go b/pkg/logentry/stages/json_test.go
index 3756dbd9011ce3d14face9ae600aa1f3b2c229d9..bc9951d2792c9f8b2643da7a5861b2b66e571cfd 100644
--- a/pkg/logentry/stages/json_test.go
+++ b/pkg/logentry/stages/json_test.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/cortexproject/cortex/pkg/util"
+	"github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v2"
 )
 
@@ -37,7 +38,7 @@ func TestYamlMapStructure(t *testing.T) {
 	}
 	want := &JSONConfig{
 		Labels: map[string]*JSONLabel{
-			"stream": &JSONLabel{
+			"stream": {
 				Source: String("json_key_name.json_sub_key_name"),
 			},
 		},
@@ -313,6 +314,7 @@ func TestJSONParser_Parse(t *testing.T) {
 	for tName, tt := range tests {
 		tt := tt
 		t.Run(tName, func(t *testing.T) {
+			t.Parallel()
 			p, err := NewJSON(util.Logger, tt.config)
 			if err != nil {
 				t.Fatalf("failed to create json parser: %s", err)
@@ -321,9 +323,7 @@ func TestJSONParser_Parse(t *testing.T) {
 			p.Process(lbs, &tt.t, &tt.entry)
 
 			assertLabels(t, tt.expectedLabels, lbs)
-			if tt.entry != tt.expectedEntry {
-				t.Fatalf("mismatch entry want: %s got:%s", tt.expectedEntry, tt.entry)
-			}
+			assert.Equal(t, tt.expectedEntry, tt.entry, "did not receive expected log entry")
 			if tt.t.Unix() != tt.expectedT.Unix() {
 				t.Fatalf("mismatch ts want: %s got:%s", tt.expectedT, tt.t)
 			}
diff --git a/pkg/logentry/stages/regex.go b/pkg/logentry/stages/regex.go
index 49e8f8a50533dc250a7815d204507e4472225b56..e44ac163f6a8c9e1ab4dadeb7c6ef68cb7997243 100644
--- a/pkg/logentry/stages/regex.go
+++ b/pkg/logentry/stages/regex.go
@@ -45,6 +45,7 @@ func newRegexConfig(config interface{}) (*RegexConfig, error) {
 	return cfg, nil
 }
 
+// Config Errors
 const (
 	ErrExpressionRequired      = "expression is required"
 	ErrCouldNotCompileRegex    = "could not compile regular expression"
@@ -116,6 +117,7 @@ type regexStage struct {
 	logger     log.Logger
 }
 
+// NewRegex creates a new regular expression based pipeline processing stage.
 func NewRegex(logger log.Logger, config interface{}) (Stage, error) {
 	cfg, err := newRegexConfig(config)
 	if err != nil {
@@ -132,6 +134,7 @@ func NewRegex(logger log.Logger, config interface{}) (Stage, error) {
 	}, nil
 }
 
+// Process implements a pipeline stage
 func (r *regexStage) Process(labels model.LabelSet, t *time.Time, entry *string) {
 	if entry == nil {
 		level.Debug(r.logger).Log("msg", "cannot parse a nil entry")
diff --git a/pkg/logentry/stages/regex_test.go b/pkg/logentry/stages/regex_test.go
index 37b039805fe064b241c613b7fd226b61b3ae9f8c..3b8ddb14e0bee61c2288bb29b4632609aa762658 100644
--- a/pkg/logentry/stages/regex_test.go
+++ b/pkg/logentry/stages/regex_test.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/cortexproject/cortex/pkg/util"
 	"github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
 	"gopkg.in/yaml.v2"
 )
 
@@ -39,7 +40,7 @@ func TestRegexMapStructure(t *testing.T) {
 	}
 	want := &RegexConfig{
 		Labels: map[string]*RegexLabel{
-			"stream": &RegexLabel{
+			"stream": {
 				Source: String("stream"),
 			},
 		},
@@ -195,9 +196,11 @@ func TestRegexConfig_validate(t *testing.T) {
 	}
 }
 
-var regexLogFixture = `11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] "GET /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"`
-var regexLogFixture_missingLabel = `2016-10-06T00:17:09.669794202Z stdout k `
-var regexLogFixture_invalidTimestamp = `2016-10-06sfsT00:17:09.669794202Z stdout k `
+var (
+	regexLogFixture                 = `11.11.11.11 - frank [25/Jan/2000:14:00:01 -0500] "GET /1986.js HTTP/1.1" 200 932 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 GTB6"`
+	regexLogFixtureMissingLabel     = `2016-10-06T00:17:09.669794202Z stdout k `
+	regexLogFixtureInvalidTimestamp = `2016-10-06sfsT00:17:09.669794202Z stdout k `
+)
 
 func TestRegexParser_Parse(t *testing.T) {
 	t.Parallel()
@@ -292,8 +295,8 @@ func TestRegexParser_Parse(t *testing.T) {
 					},
 				},
 			},
-			regexLogFixture_missingLabel,
-			regexLogFixture_missingLabel,
+			regexLogFixtureMissingLabel,
+			regexLogFixtureMissingLabel,
 			time.Now(),
 			time.Date(2016, 10, 06, 00, 17, 9, 669794202, utc),
 			nil,
@@ -315,8 +318,8 @@ func TestRegexParser_Parse(t *testing.T) {
 					},
 				},
 			},
-			regexLogFixture_missingLabel,
-			regexLogFixture_missingLabel,
+			regexLogFixtureInvalidTimestamp,
+			regexLogFixtureInvalidTimestamp,
 			time.Date(2019, 10, 06, 00, 17, 9, 0, utc),
 			time.Date(2019, 10, 06, 00, 17, 9, 0, utc),
 			nil,
@@ -352,6 +355,7 @@ func TestRegexParser_Parse(t *testing.T) {
 	for tName, tt := range tests {
 		tt := tt
 		t.Run(tName, func(t *testing.T) {
+			t.Parallel()
 			p, err := NewRegex(util.Logger, tt.config)
 			if err != nil {
 				t.Fatalf("failed to create regex parser: %s", err)
@@ -360,9 +364,7 @@ func TestRegexParser_Parse(t *testing.T) {
 			p.Process(lbs, &tt.t, &tt.entry)
 
 			assertLabels(t, tt.expectedLabels, lbs)
-			if tt.entry != tt.expectedEntry {
-				t.Fatalf("mismatch entry want: %s got:%s", tt.expectedEntry, tt.entry)
-			}
+			assert.Equal(t, tt.expectedEntry, tt.entry, "did not receive expected log entry")
 			if tt.t.Unix() != tt.expectedT.Unix() {
 				t.Fatalf("mismatch ts want: %s got:%s", tt.expectedT, tt.t)
 			}
diff --git a/pkg/logentry/stages/util_test.go b/pkg/logentry/stages/util_test.go
index 10fc7bbed48cdfdde615a7e0343a570fccdc508d..e43ba516a49ac928370f0d970085b03b624b90a5 100644
--- a/pkg/logentry/stages/util_test.go
+++ b/pkg/logentry/stages/util_test.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/prometheus/common/model"
+	"github.com/stretchr/testify/assert"
 )
 
 func mustParseTime(layout, value string) time.Time {
@@ -32,8 +33,6 @@ func assertLabels(t *testing.T, expect map[string]string, got model.LabelSet) {
 		if !ok {
 			t.Fatalf("missing expected label key: %s", k)
 		}
-		if gotV != model.LabelValue(v) {
-			t.Fatalf("mismatch label value got: %s/%s want %s/%s", k, gotV, k, model.LabelValue(v))
-		}
+		assert.Equal(t, model.LabelValue(v), gotV, "mismatch label value")
 	}
 }
diff --git a/pkg/parser/model.go b/pkg/parser/model.go
deleted file mode 100644
index 98a612a97ef6b3e833e684770c29cc4c73f89b51..0000000000000000000000000000000000000000
--- a/pkg/parser/model.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package parser
-
-type Label struct {
-	LabelName string
-	Source    string
-}
diff --git a/pkg/promtail/promtail_test.go b/pkg/promtail/promtail_test.go
index 0034af876d3dac7d67ddcc41dbad50cd835547f5..544818a0f258b353de3409daef19052a9118b94b 100644
--- a/pkg/promtail/promtail_test.go
+++ b/pkg/promtail/promtail_test.go
@@ -25,7 +25,6 @@ import (
 	"github.com/prometheus/prometheus/promql"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/grafana/loki/pkg/logentry"
 	"github.com/grafana/loki/pkg/logproto"
 	"github.com/grafana/loki/pkg/parser"
 	"github.com/grafana/loki/pkg/promtail/api"
diff --git a/pkg/promtail/scrape/scrape.go b/pkg/promtail/scrape/scrape.go
index a8140b63c83baf8f0195fddfe2bd51dbbba90dfb..d2e8402297a79876026f6812b611ab3b6a27f225 100644
--- a/pkg/promtail/scrape/scrape.go
+++ b/pkg/promtail/scrape/scrape.go
@@ -7,11 +7,13 @@ import (
 	"github.com/prometheus/prometheus/pkg/relabel"
 
 	"github.com/grafana/loki/pkg/logentry"
+	"github.com/grafana/loki/pkg/promtail/api"
 )
 
 // Config describes a job to scrape.
 type Config struct {
 	JobName                string                           `yaml:"job_name,omitempty"`
+	EntryParser            api.EntryParser                  `yaml:"entry_parser"`
 	PipelineStages         logentry.PipelineStages          `yaml:"pipeline_stages,omitempty"`
 	RelabelConfigs         []*relabel.Config                `yaml:"relabel_configs,omitempty"`
 	ServiceDiscoveryConfig sd_config.ServiceDiscoveryConfig `yaml:",inline"`
@@ -19,7 +21,7 @@ type Config struct {
 
 // DefaultScrapeConfig is the default Config.
 var DefaultScrapeConfig = Config{
-	//PipelineStages: api.Docker,
+	EntryParser: api.Docker,
 }
 
 // UnmarshalYAML implements the yaml.Unmarshaler interface.
diff --git a/pkg/promtail/targets/filetargetmanager.go b/pkg/promtail/targets/filetargetmanager.go
index 60be0bfe8fddad2f16c10fa4b6c2e31c45bb029b..e635182302ec47619795885b62c16acdb3a89acf 100644
--- a/pkg/promtail/targets/filetargetmanager.go
+++ b/pkg/promtail/targets/filetargetmanager.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/grafana/loki/pkg/helpers"
 	"github.com/grafana/loki/pkg/logentry"
+	"github.com/grafana/loki/pkg/logentry/stages"
 	"github.com/grafana/loki/pkg/promtail/api"
 	"github.com/grafana/loki/pkg/promtail/positions"
 	"github.com/grafana/loki/pkg/promtail/scrape"
@@ -74,10 +75,36 @@ func NewFileTargetManager(
 
 	config := map[string]sd_config.ServiceDiscoveryConfig{}
 	for _, cfg := range scrapeConfigs {
+
 		pipeline, err := logentry.NewPipeline(log.With(logger, "component", "pipeline"), cfg.PipelineStages)
 		if err != nil {
 			return nil, err
 		}
+
+		// Backwards compatibility with old EntryParser config
+		if pipeline.Size() == 0 {
+			switch cfg.EntryParser {
+			case api.CRI:
+				level.Warn(logger).Log("msg", "WARNING!!! entry_parser config is deprecated, please change to pipeline_stages")
+				cri, err := stages.NewCri(logger)
+				if err != nil {
+					return nil, err
+				}
+				pipeline.AddStage(cri)
+			case api.Docker:
+				level.Warn(logger).Log("msg", "WARNING!!! entry_parser config is deprecated, please change to pipeline_stages")
+				docker, err := stages.NewDocker(logger)
+				if err != nil {
+					return nil, err
+				}
+				pipeline.AddStage(docker)
+			case api.Raw:
+				level.Warn(logger).Log("msg", "WARNING!!! entry_parser config is deprecated, please change to pipeline_stages")
+			default:
+
+			}
+		}
+
 		s := &targetSyncer{
 			log:            logger,
 			positions:      positions,