diff --git a/config/config.sample.yaml b/config/config.sample.yaml
index 7b3680e3904ee8c309b57dd01f5d6d6315927d4a..10f6d2fb97e7f9ca24fff97f962683894245c11b 100644
--- a/config/config.sample.yaml
+++ b/config/config.sample.yaml
@@ -90,5 +90,7 @@ limits:
     # fininished handling it, causing us to echo it back to the room)
     discordSendDelay: 750
 ghosts:
-    # the tag to append to ghost nicknames
-    tag: ""
+    # Pattern for the ghosts nick, available is :nick, :username, :tag and :id
+    nickPattern: ":nick"
+    # Pattern for the ghosts username, available is :username, :tag and :id
+    usernamePattern: ":username#:tag"
diff --git a/config/config.schema.yaml b/config/config.schema.yaml
index 68ecba2af8dc808d77ae8ad9d7a7349874c133e0..7439ef2852c7af065ee69f2d91c8a476420b01da 100644
--- a/config/config.schema.yaml
+++ b/config/config.schema.yaml
@@ -112,5 +112,7 @@ properties:
     ghosts:
         type: "object"
         properties:
-            tag:
+            nickPattern:
+                type: "string"
+            usernamePattern:
                 type: "string"
diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts
index 8f76d9e15799f57ed29e63e61a9fd53878cce47d..c948f35da71df85004efa18ee4fbb802d0014d85 100644
--- a/src/channelsyncroniser.ts
+++ b/src/channelsyncroniser.ts
@@ -163,14 +163,10 @@ export class ChannelSyncroniser {
             return channelState;
         }
 
-        const patternMap = {
+        const name: string = Util.ApplyPatternString(this.config.channel.namePattern, {
             guild: channel.guild.name,
             name: "#" + channel.name,
-        };
-        let name: string = this.config.channel.namePattern;
-        for (const p of Object.keys(patternMap)) {
-            name = name.replace(new RegExp(":" + p, "g"), patternMap[p]);
-        }
+        });
         const topic = channel.topic;
         const icon = channel.guild.icon;
         let iconUrl: string | null = null;
diff --git a/src/config.ts b/src/config.ts
index b54ca4cf02da2d5f5e0512e2da1e487a988b4d4a..637bc555fb28450a2b2feccb5eb584eaa2c114fe 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -112,5 +112,6 @@ export class LoggingFile {
 }
 
 class DiscordBridgeConfigGhosts {
-    public tag: string = "";
+    public nickPattern: string = ":nick";
+    public usernamePattern: string = ":username#:tag";
 }
diff --git a/src/usersyncroniser.ts b/src/usersyncroniser.ts
index 2af4dac34fa821e2b5fe3d28622f163dee20afa3..86e144b3bb9d999128bf47c52408764b0fa8a956 100644
--- a/src/usersyncroniser.ts
+++ b/src/usersyncroniser.ts
@@ -234,8 +234,11 @@ export class UserSyncroniser {
             id: discordUser.id,
             mxUserId: `@_discord_${discordUser.id}${mxidExtra}:${this.config.bridge.domain}`,
         });
-        const displayName = this.displayNameForUser(discordUser)
-            + (this.config.ghosts.tag ? " " + this.config.ghosts.tag : "");
+        const displayName = Util.ApplyPatternString(this.config.ghosts.usernamePattern, {
+            id: discordUser.id,
+            tag: discordUser.discriminator,
+            username: discordUser.username,
+        });
         // Determine if the user exists.
         const remoteId = discordUser.id + mxidExtra;
         const remoteUser = await this.userStore.getRemoteUser(remoteId);
@@ -271,10 +274,16 @@ export class UserSyncroniser {
     public async GetUserStateForGuildMember(
         newMember: GuildMember,
     ): Promise<IGuildMemberState> {
+        const name = Util.ApplyPatternString(this.config.ghosts.nickPattern, {
+            id: newMember.user.id,
+            nick: newMember.displayName,
+            tag: newMember.user.discriminator,
+            username: newMember.user.username,
+        });
         const guildState: IGuildMemberState = Object.assign({}, DEFAULT_GUILD_STATE, {
             bot: newMember.user.bot,
             displayColor: newMember.displayColor,
-            displayName: newMember.displayName + (this.config.ghosts.tag ? " " + this.config.ghosts.tag : ""),
+            displayName: name,
             id: newMember.id,
             mxUserId: `@_discord_${newMember.id}:${this.config.bridge.domain}`,
             roles: newMember.roles.map((role) => { return {
@@ -395,10 +404,6 @@ export class UserSyncroniser {
         });
     }
 
-    private displayNameForUser(discordUser): string {
-        return `${discordUser.username}#${discordUser.discriminator}`;
-    }
-
     private async leave(intent: Intent, roomId: string, checkCache: boolean = true) {
         const userId = intent.getClient().getUserId();
         if (checkCache && ![null, "join", "invite"]
diff --git a/src/util.ts b/src/util.ts
index 8b606bbf40b61d67562c244414ab7c42a7265b8f..aca4d651a2eb9780ff8d1a5666b389799edb10db 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -47,6 +47,10 @@ export interface ICommandParameters {
     [index: string]: ICommandParameter;
 }
 
+export interface IPatternMap {
+    [index: string]: string;
+}
+
 export class Util {
     /**
      * downloadFile - This function will take a URL and store the resulting data into
@@ -274,6 +278,13 @@ export class Util {
         const htmlColor = pad.substring(0, pad.length - colorHex.length) + colorHex;
         return htmlColor;
     }
+
+    public static ApplyPatternString(str: string, patternMap: IPatternMap): string {
+        for (const p of Object.keys(patternMap)) {
+            str = str.replace(new RegExp(":" + p, "g"), patternMap[p]);
+        }
+        return str;
+    }
 }
 
 interface IUploadResult {
diff --git a/test/test_channelsyncroniser.ts b/test/test_channelsyncroniser.ts
index c54aa2f1cd7e4ce0e728aa33428452351f95f691..7d90f6e827c782c1ca6f4ce11d618af0dd6ed124 100644
--- a/test/test_channelsyncroniser.ts
+++ b/test/test_channelsyncroniser.ts
@@ -24,6 +24,7 @@ import { MockGuild } from "./mocks/guild";
 import { MockMember } from "./mocks/member";
 import { MatrixEventProcessor, MatrixEventProcessorOpts } from "../src/matrixeventprocessor";
 import { DiscordBridgeConfig } from "../src/config";
+import { Util } from "../src/util";
 import { MockChannel } from "./mocks/channel";
 import { Bridge, MatrixRoom, RemoteRoom } from "matrix-appservice-bridge";
 // we are a test file and thus need those
@@ -44,6 +45,7 @@ let ROOM_DIRECTORY_VISIBILITY: any = null;
 const ChannelSync = (Proxyquire("../src/channelsyncroniser", {
     "./util": {
         Util: {
+            ApplyPatternString: Util.ApplyPatternString,
             UploadContentFromUrl: async () => {
                 UTIL_UPLOADED_AVATAR = true;
                 return {mxcUrl: "avatarset"};
diff --git a/test/test_usersyncroniser.ts b/test/test_usersyncroniser.ts
index 2984d293e8d77b25b4e98971ed0d4ac1673f68c9..531f3e86e1ded68fb2848882d750c9e195f128b9 100644
--- a/test/test_usersyncroniser.ts
+++ b/test/test_usersyncroniser.ts
@@ -55,6 +55,7 @@ const GUILD_ROOM_IDS_WITH_ROLE = ["!abc:localhost", "!def:localhost"];
 const UserSync = (Proxyquire("../src/usersyncroniser", {
     "./util": {
         Util: {
+            ApplyPatternString: Util.ApplyPatternString,
             AsyncForEach: Util.AsyncForEach,
             UploadContentFromUrl: async () => {
                 UTIL_UPLOADED_AVATAR = true;
@@ -64,7 +65,7 @@ const UserSync = (Proxyquire("../src/usersyncroniser", {
     },
 })).UserSyncroniser;
 
-function CreateUserSync(remoteUsers: RemoteUser[] = [], nickTag: string = ""): UserSyncroniser {
+function CreateUserSync(remoteUsers: RemoteUser[] = [], ghostConfig: any = {}): UserSyncroniser {
     UTIL_UPLOADED_AVATAR = false;
     SEV_ROOM_ID = null;
     SEV_CONTENT = null;
@@ -153,7 +154,7 @@ function CreateUserSync(remoteUsers: RemoteUser[] = [], nickTag: string = ""): U
     };
     const config = new DiscordBridgeConfig();
     config.bridge.domain = "localhost";
-    config.ghosts.tag = nickTag;
+    config.ghosts = Object.assign({}, config.ghosts, ghostConfig);
     return new UserSync(bridge as Bridge, config, discordbot, userStore as any);
 }
 
@@ -197,12 +198,12 @@ describe("UserSyncroniser", () => {
             expect(state.avatarId, "AvatarID").is.empty;
             expect(state.avatarUrl, "AvatarUrl").is.null;
         });
-        it("Will obay name tags", async () => {
+        it("Will obay name patterns", async () => {
             const remoteUser = new RemoteUser("123456");
             remoteUser.avatarurl = "test.jpg";
             remoteUser.displayname = "TestUsername";
 
-            const userSync = CreateUserSync([remoteUser], "(Discord)");
+            const userSync = CreateUserSync([remoteUser], {usernamePattern: ":username#:tag (Discord)"});
             const user = new MockUser(
                 "123456",
                 "TestUsername",
@@ -489,8 +490,8 @@ describe("UserSyncroniser", () => {
             const state = await userSync.GetUserStateForGuildMember(member as any);
             expect(state.displayName).to.be.equal("BestDog");
         });
-        it("Will will obay name tags", async () => {
-            const userSync = CreateUserSync([new RemoteUser("123456")], "(Discord)");
+        it("Will will obay nick pattern", async () => {
+            const userSync = CreateUserSync([new RemoteUser("123456")], { nickPattern: ":nick (Discord)" });
             const guild = new MockGuild(
                 "654321");
             const member = new MockMember(