diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -122,6 +122,9 @@
 | [DL3019](https://github.com/hadolint/hadolint/wiki/DL3019)   | Use the `--no-cache` switch to avoid the need to use `--update` and remove `/var/cache/apk/*` when done installing packages.                        |
 | [DL3020](https://github.com/hadolint/hadolint/wiki/DL3020)   | Use `COPY` instead of `ADD` for files and folders.                                                                                                  |
 | [DL3021](https://github.com/hadolint/hadolint/wiki/DL3021)   | `COPY` with more than 2 arguments requires the last argument to end with `/`                                                                                                |
+| [DL3022](https://github.com/hadolint/hadolint/wiki/DL3022)   | `COPY --from` should reference a previously defined `FROM` alias                                                                                    |
+| [DL3023](https://github.com/hadolint/hadolint/wiki/DL3023)   | `COPY --from` cannot reference its own `FROM` alias                                                                                                 |
+| [DL3024](https://github.com/hadolint/hadolint/wiki/DL3024)   | `FROM` aliases (stage names) must be unique                                                                                                         |
 | [DL4000](https://github.com/hadolint/hadolint/wiki/DL4000)   | MAINTAINER is deprecated.                                                                                                                           |
 | [DL4001](https://github.com/hadolint/hadolint/wiki/DL4001)   | Either use Wget or Curl but not both.                                                                                                               |
 | [DL4003](https://github.com/hadolint/hadolint/wiki/DL4003)   | Multiple `CMD` instructions found.                                                                                                                  |
diff --git a/hadolint.cabal b/hadolint.cabal
--- a/hadolint.cabal
+++ b/hadolint.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 36374cf97e8c22f6971eb0142316c4cb1e42f9d83fba7b06c6558bb0ce1ccf24
+-- hash: 27e3b32590f30c0792f229a5aebdd2796f20c9ef9889029a1f2c6dbd2d986bcf
 
 name:           hadolint
-version:        1.3.0
+version:        1.4.0
 synopsis:       Dockerfile Linter JavaScript API
 description:    A smarter Dockerfile linter that helps you build best practice Docker images.
 category:       Development
diff --git a/src/Hadolint/Rules.hs b/src/Hadolint/Rules.hs
--- a/src/Hadolint/Rules.hs
+++ b/src/Hadolint/Rules.hs
@@ -1,6 +1,8 @@
 module Hadolint.Rules where
 
-import Data.List (intercalate, isInfixOf, isPrefixOf, isSuffixOf)
+import Control.Arrow ((&&&))
+import Data.List
+       (intercalate, isInfixOf, isPrefixOf, isSuffixOf, mapAccumL)
 import Data.List.NonEmpty (toList)
 import Data.List.Split (splitOn, splitOneOf)
 import Data.Maybe (fromMaybe, isJust, mapMaybe)
@@ -41,14 +43,35 @@
 
 -- Apply a function on each instruction and create a check
 -- for the according line number
-mapInstructions :: Metadata -> (Instruction -> Bool) -> Rule
-mapInstructions metadata f = map applyRule
+mapInstructions ::
+       Metadata -> (state -> Linenumber -> Instruction -> (state, Bool)) -> state -> Rule
+mapInstructions metadata f initialState dockerfile =
+    let (_, results) = mapAccumL applyRule initialState dockerfile
+    in results
   where
-    applyRule (InstructionPos i source linenumber) = RuleCheck metadata source linenumber (f i)
+    applyRule state (InstructionPos i source linenumber) =
+        let (newState, res) = f state linenumber i
+        in (newState, RuleCheck metadata source linenumber res)
 
 instructionRule :: String -> Severity -> String -> (Instruction -> Bool) -> Rule
-instructionRule code severity message = mapInstructions $ Metadata code severity message
+instructionRule code severity message check =
+    instructionRuleLine code severity message (const check)
 
+instructionRuleLine :: String -> Severity -> String -> (Linenumber -> Instruction -> Bool) -> Rule
+instructionRuleLine code severity message check =
+    instructionRuleState code severity message checkAndDropState ()
+  where
+    checkAndDropState state line instr = (state, check line instr)
+
+instructionRuleState ::
+       String
+    -> Severity
+    -> String
+    -> (state -> Linenumber -> Instruction -> (state, Bool))
+    -> state
+    -> Rule
+instructionRuleState code severity message = mapInstructions (Metadata code severity message)
+
 dockerfileRule :: String -> Severity -> String -> ([Instruction] -> Bool) -> Rule
 dockerfileRule code severity message f = rule
   where
@@ -69,6 +92,9 @@
     , invalidCmd
     , copyInsteadAdd
     , copyEndingSlash
+    , copyFromExists
+    , copyFromAnother
+    , fromAliasUnique
     , noRootUser
     , noCd
     , noSudo
@@ -112,6 +138,31 @@
 bashCommands :: [String] -> [[String]]
 bashCommands = splitOneOf [";", "|", "&&"]
 
+allAliasedImages :: Dockerfile -> [(Linenumber, ImageAlias)]
+allAliasedImages dockerfile =
+    [(l, a) | (l, From (UntaggedImage _ (Just a))) <- instr] ++
+    [(l, a) | (l, From (TaggedImage _ _ (Just a))) <- instr] ++
+    [(l, a) | (l, From (DigestedImage _ _ (Just a))) <- instr]
+  where
+    instr = fmap (lineNumber &&& instruction) dockerfile
+
+-- | Returns a list of all image aliases in FROM instructions that
+--  are defined before the given line number.
+previouslyDefinedAliases :: Linenumber -> Dockerfile -> [String]
+previouslyDefinedAliases line dockerfile =
+    [i | (l, ImageAlias i) <- allAliasedImages dockerfile, l < line]
+
+-- | Returns the result of running the check function on the image alias
+--   name, if the passed instruction is a FROM instruction with a stage alias.
+--   Otherwise, returns True.
+aliasMustBe :: (String -> Bool) -> Instruction -> Bool
+aliasMustBe predicate fromInstr =
+    case fromInstr of
+        From (UntaggedImage _ (Just (ImageAlias alias))) -> predicate alias
+        From (TaggedImage _ _ (Just (ImageAlias alias))) -> predicate alias
+        From (DigestedImage _ _ (Just (ImageAlias alias))) -> predicate alias
+        _ -> True
+
 absoluteWorkdir = instructionRule code severity message check
   where
     code = "DL3000"
@@ -211,14 +262,14 @@
     check (Run args) = not $ isInfixOf ["apt-get", "upgrade"] args
     check _ = True
 
-noUntagged = instructionRule code severity message check
+noUntagged dockerfile = instructionRuleLine code severity message check dockerfile
   where
     code = "DL3006"
     severity = WarningC
     message = "Always tag the version of an image explicitly."
-    check (From (UntaggedImage image _)) = image == "scratch"
-    check (From TaggedImage {}) = True
-    check _ = True
+    check line (From (UntaggedImage "scratch" _)) = True
+    check line (From (UntaggedImage i _)) = i `elem` previouslyDefinedAliases line dockerfile
+    check _ _ = True
 
 noLatestTag = instructionRule code severity message check
   where
@@ -474,6 +525,36 @@
         | otherwise = True
     check _ = True
     endsWithSlash (TargetPath t) = last t == '/' -- it is safe to use last, as the target is never empty
+
+copyFromExists dockerfile = instructionRuleLine code severity message check dockerfile
+  where
+    code = "DL3022"
+    severity = WarningC
+    message = "COPY --from should reference a previously defined FROM alias"
+    check l (Copy (CopyArgs _ _ _ (CopySource s))) = s `elem` previouslyDefinedAliases l dockerfile
+    check _ _ = True
+
+copyFromAnother = instructionRuleState code severity message check Nothing
+  where
+    code = "DL3023"
+    severity = ErrorC
+    message = "COPY --from should reference a previously defined FROM alias"
+    withState st res = (st, res)
+    -- | 'check' returns a tuple (state, check_result)
+    --   The state in this case is the FROM instruction where the current instruction we are
+    --   inspecting is nested in.
+    check _ _ f@(From _) = withState (Just f) True -- Remember the last FROM instruction found
+    check st@(Just fromInstr) _ (Copy (CopyArgs _ _ _ (CopySource stageName))) =
+        withState st (aliasMustBe (/= stageName) fromInstr) -- Cannot copy from itself!
+    check state _ _ = withState state True
+
+fromAliasUnique dockerfile = instructionRuleLine code severity message check dockerfile
+  where
+    code = "DL3024"
+    severity = ErrorC
+    message = "FROM aliases (stage names) must be unique"
+    check line = aliasMustBe (not . alreadyTaken line)
+    alreadyTaken line alias = alias `elem` previouslyDefinedAliases line dockerfile
 
 useShell = instructionRule code severity message check
   where
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -23,7 +23,27 @@
                     "FROM debian@sha256:\
                     \7959ed6f7e35f8b1aaa06d1d8259d4ee25aa85a086d5c125480c333183f9deeb"
             it "explicit tagged with name" $
-                ruleCatchesNot noLatestTag "FROM debian:jessie AS builder"
+              ruleCatchesNot noLatestTag "FROM debian:jessie AS builder"
+            it "local aliases are OK to be untagged" $
+                let dockerfile =
+                        [ "FROM golang:1.9.3-alpine3.7 AS build"
+                        , "RUN foo"
+                        , "FROM build as unit-test"
+                        , "RUN bar"
+                        , "FROM alpine:3.7"
+                        , "RUN baz"
+                        ]
+                in ruleCatchesNot noUntagged $ unlines dockerfile
+            it "other untagged cases are not ok" $
+                let dockerfile =
+                        [ "FROM golang:1.9.3-alpine3.7 AS build"
+                        , "RUN foo"
+                        , "FROM node as unit-test"
+                        , "RUN bar"
+                        , "FROM alpine:3.7"
+                        , "RUN baz"
+                        ]
+                in ruleCatches noUntagged $ unlines dockerfile
         --
         describe "no root or sudo rules" $ do
             it "sudo" $ ruleCatches noSudo "RUN sudo apt-get update"
@@ -266,6 +286,61 @@
             it "no warn on 2 args" $ ruleCatchesNot copyEndingSlash "COPY foo bar"
             it "warn on 3 args" $ ruleCatches copyEndingSlash "COPY foo bar baz"
             it "no warn on 3 args" $ ruleCatchesNot copyEndingSlash "COPY foo bar baz/"
+        --
+        describe "copy from existing alias" $ do
+            it "warn on missing alias" $ ruleCatches copyFromExists "COPY --from=foo bar ."
+            it "warn on alias defined after" $
+                let dockerfile =
+                        [ "FROM scratch"
+                        , "COPY --from=build foo ."
+                        , "FROM node as build"
+                        , "RUN baz"
+                        ]
+                in ruleCatches copyFromExists $ unlines dockerfile
+            it "don't warn on correctly defined aliases" $
+                let dockerfile =
+                        [ "FROM scratch as build"
+                        , "RUN foo"
+                        , "FROM node"
+                        , "COPY --from=build foo ."
+                        , "RUN baz"
+                        ]
+                in ruleCatchesNot copyFromExists $ unlines dockerfile
+        --
+        describe "copy from own FROM" $ do
+            it "warn on copying from your the same FROM" $
+                let dockerfile =
+                        [ "FROM node as foo"
+                        , "COPY --from=foo bar ."
+                        ]
+                in ruleCatches copyFromAnother $ unlines dockerfile
+            it "don't warn on copying form other sources" $
+                let dockerfile =
+                        [ "FROM scratch as build"
+                        , "RUN foo"
+                        , "FROM node as run"
+                        , "COPY --from=build foo ."
+                        , "RUN baz"
+                        ]
+                in ruleCatchesNot copyFromAnother $ unlines dockerfile
+        --
+        describe "Duplicate aliases" $ do
+            it "warn on duplicate aliases" $
+                let dockerfile =
+                        [ "FROM node as foo"
+                        , "RUN something"
+                        , "FROM scratch as foo"
+                        , "RUN something"
+                        ]
+                in ruleCatches fromAliasUnique $ unlines dockerfile
+            it "don't warn on unique aliases" $
+                let dockerfile =
+                        [ "FROM scratch as build"
+                        , "RUN foo"
+                        , "FROM node as run"
+                        , "RUN baz"
+                        ]
+                in ruleCatchesNot fromAliasUnique $ unlines dockerfile
         --
         describe "format error" $
             it "display error after line pos" $ do
