You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2022/04/20 01:26:52 UTC

[trafficcontrol] branch master updated: Layered profile `servers/` API {GET, POST, PUT & DELETE} (#6544)

This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new acce8f1884 Layered profile `servers/` API {GET, POST,  PUT & DELETE} (#6544)
acce8f1884 is described below

commit acce8f18844dd68ea1346d6f781735f3ba906ee4
Author: Rima Shah <22...@users.noreply.github.com>
AuthorDate: Tue Apr 19 19:26:46 2022 -0600

    Layered profile `servers/` API {GET, POST,  PUT & DELETE} (#6544)
    
    * Updated server struct and type for V4 and added commonpropertiesV40 function and struct
    
    * Added db migration files
    
    * Added backward compatibility to get (GET) and update (PUT) /server
    
    * Updated docs for GET /server and PUT /server/{id}
    
    * Updated migration script, doc, /server API endpoint for GET and PUT
    
    * Updated query for profile_names
    
    * Updated profile_names type
    
    * Passing in the original transaction id
    
    * Update servers.go for PUT servers/{id} API
    
    * Moved function having db transactions to dbhelpers.
    
    * Fixed go vet
    
    * Fixed testing/api/v4 test
    
    * Updated cache config and lib test
    
    * Fixed fmt to log
    
    * Fixed server profile to profiles
    
    * Updated docs and tests(cache-config).
    
    * Updated tests to accept first profile to ensure current tests pass.
    
    * Updated tests to accept first profile to ensure current tests pass-1.
    
    * Added changelog entry
    
    * Fixed unit tests
    
    * Removed fmt statement.
    
    * Tc_fixture updated for profiles (v4).
    
    * Added logic for POST servers/
    
    * Updated server_profile foreign key constraint for delete servers/{id} API
    
    * Updated db_helper functions to return err
    
    * Updated parentdotconfig.go and server.go for profile
    
    * Fixed test related to profile
    
    * Updated API/v4 test related to profile.
    Updated tc_fixtures tp remove duplicate interface ip.
    
    * Removed extra if clause.
    
    * Removed database transaction from api/v4/ tests
    
    * Updated changelog and server params
    
    * Removed fmt statements
    
    * updated select statement for v2/v3 and added logic to fill in server_profile table for v2/v3 when creating a server
    
    * replaced profileId with profileNames in traffic portal data
    
    * replaced profileId with profileNames in traffic portal data-1
    
    * Removed changes from checkTypeChangeSafety() in servers.go
    
    * Updated unit test for servers_test.go and added multiple profiles to one of the servers in v4/tc_fixtures
    
    * Changed enroller server_template.json for CIAB
    
    * Traffic Portal changes
    
    * Removed db_helpers function from cache-config
    
    * Updated TP server's integration test
    
    * Removed get profile name request by profile ID since we already have a profile name.
    
    * updated profile field in server
    
    * updated error message in validatedCommonV40 and server integration tests
    
    * updated API.ts to add random characters for profileNames
    
    * updated APIv4 docs
    
    * Made profile-names singular to match current documentation.
    
    * Update based on review comments.
    
    * Changed Profiles field in go struct to ProfileNames to match JSON.
    
    * Changed ProfileNames field type in struct ServerV40 to []string from pq.StringArray
    
    * Removed additional code from TP.
    
    * Updated code and queries for PUT and POST call.
    
    * Updated function call param
    
    * Changed CommonServerPropertiesV40 field ProfileNames from a pointer of string slice to a slice of string. Created new migration file to make them as latest date.
    
    * Created new migration file to make them as latest date.
    
    * Updated conversion.go
    
    * Fixed queries and scan() to correct GH failures.
    
    * fixed review comment
    
    * fixed review comment-1
    
    * fixed review comment-2
    
    * fixed merge conflict
    
    * fixed TP and unit test.
    
    * updated based on review comments.
    
    * removed triggers for server and ip_address tables.
    
    * updated db query to check uniqueness of ip address and profile.
    
    * updated based on latest review comments (migration file now on a latest date and updated seed.sql for Dev CIAB and simplified logic to insert in server_profile)
    
    * review change.
    
    * Changed DB array comparison from (<@ and @>) to (=)
    
    * Removed `updPending` from `validateCommonV40` to be in sync with `T3C Race Condition Update` commit
    
    * error wrapping.
    
    * using errorIs
    
    * Removed CommonServerPropertiesV40
    
    * Changed http code for validateV4()-1
---
 CHANGELOG.md                                       |   1 +
 blueprints/layered-profile-server.md               |  19 +-
 cache-config/t3c-generate/cfgfile/cfgfile_test.go  |   4 +-
 cache-config/t3cutil/getdata.go                    |  12 +-
 cache-config/t3cutil/getdatacfg.go                 |  20 +-
 cache-config/t3cutil/toreq/toreqold/conversions.go |   2 +-
 dev/traffic_ops/seed.psql                          |   5 +
 docs/source/api/v4/servers.rst                     |  20 +-
 docs/source/api/v4/servers_id.rst                  |  20 +-
 .../cdn-in-a-box/enroller/server_template.json     |   2 +-
 lib/go-atscfg/astatsdotconfig.go                   |   2 +-
 lib/go-atscfg/atscfg_test.go                       |   4 +-
 lib/go-atscfg/atsdotrules.go                       |   2 +-
 lib/go-atscfg/atsdotrules_test.go                  |   2 +-
 lib/go-atscfg/cachedotconfig.go                    |  17 +-
 lib/go-atscfg/cachedotconfig_test.go               |   2 +-
 lib/go-atscfg/dropqstringdotconfig.go              |   2 +-
 lib/go-atscfg/dropqstringdotconfig_test.go         |   2 +-
 lib/go-atscfg/facts.go                             |   6 +-
 lib/go-atscfg/facts_test.go                        |   2 +-
 lib/go-atscfg/hostingdotconfig_test.go             |  18 +-
 lib/go-atscfg/loggingdotconfig.go                  |   6 +-
 lib/go-atscfg/loggingdotconfig_test.go             |   2 +-
 lib/go-atscfg/loggingdotyaml.go                    |   6 +-
 lib/go-atscfg/loggingdotyaml_test.go               |   4 +-
 lib/go-atscfg/logsdotxml.go                        |   2 +-
 lib/go-atscfg/logsdotxml_test.go                   |   2 +-
 lib/go-atscfg/meta.go                              |   4 +-
 lib/go-atscfg/meta_test.go                         |   6 +-
 lib/go-atscfg/parentdotconfig.go                   |  29 +-
 lib/go-atscfg/parentdotconfig_test.go              |   4 +-
 lib/go-atscfg/plugindotconfig.go                   |   4 +-
 lib/go-atscfg/plugindotconfig_test.go              |   2 +-
 lib/go-atscfg/recordsdotconfig.go                  |   4 +-
 lib/go-atscfg/recordsdotconfig_test.go             |  10 +-
 lib/go-atscfg/remapdotconfig_test.go               | 216 ++++----
 lib/go-atscfg/serverunknown_test.go                |   4 +-
 lib/go-atscfg/sslservernamedotyaml.go              |   4 +-
 lib/go-atscfg/storagedotconfig.go                  |  10 +-
 lib/go-atscfg/storagedotconfig_test.go             |  16 +-
 lib/go-atscfg/sysctldotconf.go                     |   4 +-
 lib/go-atscfg/sysctldotconf_test.go                |   4 +-
 lib/go-atscfg/urlsigconfig.go                      |   4 +-
 lib/go-atscfg/urlsigconfig_test.go                 |   4 +-
 lib/go-atscfg/volumedotconfig.go                   |   4 +-
 lib/go-atscfg/volumedotconfig_test.go              |  20 +-
 lib/go-tc/servers.go                               |  99 +++-
 lib/go-tc/servers_test.go                          |  10 +-
 ...2041410185700_add_server_profile_table.down.sql |  36 ++
 ...022041410185700_add_server_profile_table.up.sql |  38 ++
 .../v4/cdn_queue_updates_by_type_profile_test.go   |   4 +-
 traffic_ops/testing/api/v4/servers_test.go         |  78 +--
 traffic_ops/testing/api/v4/tc-fixtures.json        | 130 ++---
 .../traffic_ops_golang/dbhelpers/db_helpers.go     | 111 ++++
 traffic_ops/traffic_ops_golang/server/servers.go   | 617 ++++++++++++++++++---
 .../traffic_ops_golang/server/servers_test.go      | 381 +++++++------
 traffic_ops/v4-client/server.go                    |  12 -
 .../modules/form/server/FormServerController.js    |   9 +
 .../modules/form/server/form.server.tpl.html       |   4 +-
 .../table/servers/TableServersController.js        |   5 +-
 .../app/src/modules/private/servers/new/index.js   |   3 +-
 traffic_portal/test/integration/CommonUtils/API.ts |   5 +
 .../test/integration/Data/deliveryservices.ts      |   8 +-
 .../test/integration/Data/physlocations.ts         |   8 +-
 traffic_portal/test/integration/Data/servers.ts    |  32 +-
 .../integration/Data/serverservercapabilities.ts   |  48 +-
 traffic_portal/test/integration/Data/topologies.ts |  16 +-
 67 files changed, 1427 insertions(+), 766 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc498e1427..30e5270370 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - Traffic Monitor: Add support for `access.log` to TM.
 - Added functionality for login to provide a Bearer token and for that token to be later used for authorization.
 - [Traffic Ops | Traffic Go Clients | T3C] Add additional timestamp fields to server for queuing and dequeueing config and revalidate updates.
+- Added layered profile feature to 4.0 for `GET` servers/, `POST` servers/, `PUT` servers/{id} and `DELETE` servers/{id}.
 
 ### Fixed
 - Update traffic\_portal dependencies to mitigate `npm audit` issues.
diff --git a/blueprints/layered-profile-server.md b/blueprints/layered-profile-server.md
index 6b17b9d4db..26ebaec231 100644
--- a/blueprints/layered-profile-server.md
+++ b/blueprints/layered-profile-server.md
@@ -93,7 +93,7 @@ With Layered Profiles, hundreds of profiles become a few dozen, each representin
 {
   "response": [{
     "id": 5,
-    "profiles": ["MID", "AMIGA_123", "CDN_FOO"]
+    "profileNames": ["MID", "AMIGA_123", "CDN_FOO"]
   }]
 }
 ```
@@ -102,7 +102,7 @@ With Layered Profiles, hundreds of profiles become a few dozen, each representin
 ```JSON
 {
   "response": [{
-    "profiles": ["MID", "AMIGA_123", "CDN_FOO"]
+    "profileNames": ["MID", "AMIGA_123", "CDN_FOO"]
   }]
 }
 ```
@@ -115,7 +115,7 @@ JSON **request** with the proposed change will look as follows:
 {
     "cachegroupId": 6,
     "cdnId": 2,
-    "profiles": ["MID", "AMIGA_123", "CDN_FOO"]
+    "profileNames": ["MID", "AMIGA_123", "CDN_FOO"]
 }
 ```
 
@@ -124,7 +124,7 @@ JSON **request** with the proposed change will look as follows:
 {
     "cachegroupId": 6,
     "cdnId": 2,
-    "profiles": ["MID", "AMIGA_123", "CDN_FOO"]
+    "profileNames": ["MID", "AMIGA_123", "CDN_FOO"]
 }
 ```
 
@@ -141,7 +141,7 @@ return following **response**
     "cachegroupId": 6,
     "cdnId": 2,
     "id": 5,
-    "profiles": ["MID", "AMIGA_123", "CDN_FOO"]
+    "profileNames": ["MID", "AMIGA_123", "CDN_FOO"]
   }
 }
 ```
@@ -151,7 +151,7 @@ The following table describes the top level `layered_profile` object for servers
 | field           | type                 | optionality | description                                              |
 | ----------------| ---------------------| ----------- | ---------------------------------------------------------|
 | server          | bigint               | required    | the server id associated with a given profile            |
-| profiles        | text []              | required    | the profile names associated with a server               |
+| profile_name    | text                 | required    | the profile name associated with a server                |
 | order           | bigint               | required    | the order in which a profile is applied to a server      |
 
 **API constraints**
@@ -176,10 +176,10 @@ The following table describes the top level `layered_profile` object for servers
  profile_name  | text                     |           | not null |
  order         | bigint                   |           | not null |
 Indexes:
-    "pk_server_profile" PRIMARY KEY(profile_name, server, order)
+    "pk_server_profile" PRIMARY KEY(profile_name, server)
 Foreign-key constraints:
-    "fk_server" FOREIGN KEY (server) REFERENCES server(id)
-    "fk_server" FOREIGN KEY (profile_name) REFERENCES profile(name)
+    "fk_server_id" FOREIGN KEY (server) REFERENCES public.server(id) ON DELETE CASCADE ON UPDATE CASCADE
+    "fk_server_profile_name_profile" FOREIGN KEY (profile_name) REFERENCES public.profile(name) ON DELETE RESTRICT ON UPDATE CASCADE,
 ```
 
 All profiles assigned to a given server will have the same values of:
@@ -217,7 +217,6 @@ We do not anticipate any impact on security.
 
 ### Upgrade Impact
 - A Database Migration to:
-  - drop profile column in existing server table
   - insert existing server profiles along with their order into the new table(server_profiles)
 
 ### Operations Impact
diff --git a/cache-config/t3c-generate/cfgfile/cfgfile_test.go b/cache-config/t3c-generate/cfgfile/cfgfile_test.go
index 363d0a4afe..6e633a1353 100644
--- a/cache-config/t3c-generate/cfgfile/cfgfile_test.go
+++ b/cache-config/t3c-generate/cfgfile/cfgfile_test.go
@@ -311,9 +311,7 @@ func randServer() *atscfg.Server {
 	sv.OfflineReason = randStr()
 	sv.PhysLocation = randStr()
 	sv.PhysLocationID = randInt()
-	sv.Profile = randStr()
-	sv.ProfileDesc = randStr()
-	sv.ProfileID = randInt()
+	sv.ProfileNames = []string{*randStr()}
 	sv.Rack = randStr()
 	sv.RevalPending = randBool()
 	sv.Status = randStr()
diff --git a/cache-config/t3cutil/getdata.go b/cache-config/t3cutil/getdata.go
index 419cb8db0a..746262551e 100644
--- a/cache-config/t3cutil/getdata.go
+++ b/cache-config/t3cutil/getdata.go
@@ -157,12 +157,12 @@ func GetPackages(cfg TCCfg) ([]Package, error) {
 	server, _, err := cfg.TOClient.GetServerByHostName(string(cfg.CacheHostName), nil)
 	if err != nil {
 		return nil, errors.New("getting server: " + err.Error())
-	} else if server.Profile == nil {
+	} else if len(server.ProfileNames) == 0 {
 		return nil, errors.New("getting server: nil profile")
 	}
-	params, _, err := cfg.TOClient.GetServerProfileParameters(*server.Profile, nil)
+	params, _, err := cfg.TOClient.GetServerProfileParameters(server.ProfileNames[0], nil)
 	if err != nil {
-		return nil, errors.New("getting server profile '" + *server.Profile + "' parameters: " + err.Error())
+		return nil, errors.New("getting server profile '" + server.ProfileNames[0] + "' parameters: " + err.Error())
 	}
 	packages := []Package{}
 	for _, param := range params {
@@ -196,12 +196,12 @@ func GetChkconfig(cfg TCCfg) ([]ChkConfigEntry, error) {
 	server, _, err := cfg.TOClient.GetServerByHostName(string(cfg.CacheHostName), nil)
 	if err != nil {
 		return nil, errors.New("getting server: " + err.Error())
-	} else if server.Profile == nil {
+	} else if len(server.ProfileNames) == 0 {
 		return nil, errors.New("getting server: nil profile")
 	}
-	params, _, err := cfg.TOClient.GetServerProfileParameters(*server.Profile, nil)
+	params, _, err := cfg.TOClient.GetServerProfileParameters(server.ProfileNames[0], nil)
 	if err != nil {
-		return nil, errors.New("getting server profile '" + *server.Profile + "' parameters: " + err.Error())
+		return nil, errors.New("getting server profile '" + server.ProfileNames[0] + "' parameters: " + err.Error())
 	}
 	chkconfig := []ChkConfigEntry{}
 	for _, param := range params {
diff --git a/cache-config/t3cutil/getdatacfg.go b/cache-config/t3cutil/getdatacfg.go
index 608bd3f8ba..39c780d64e 100644
--- a/cache-config/t3cutil/getdatacfg.go
+++ b/cache-config/t3cutil/getdatacfg.go
@@ -272,7 +272,7 @@ func GetConfigData(toClient *toreq.TOClient, disableProxy bool, cacheHostName st
 			return errors.New("server '" + cacheHostName + " missing CDNName")
 		} else if server.CDNID == nil {
 			return errors.New("server '" + cacheHostName + " missing CDNID")
-		} else if server.Profile == nil {
+		} else if len(server.ProfileNames) == 0 {
 			return errors.New("server '" + cacheHostName + " missing Profile")
 		}
 
@@ -468,15 +468,15 @@ func GetConfigData(toClient *toreq.TOClient, disableProxy bool, cacheHostName st
 			defer func(start time.Time) { log.Infof("serverParamsF took %v\n", time.Since(start)) }(time.Now())
 			{
 				reqHdr := (http.Header)(nil)
-				if oldCfg != nil && oldServer.Profile != nil && *oldServer.Profile == *server.Profile {
+				if oldCfg != nil && len(oldServer.ProfileNames) != 0 && oldServer.ProfileNames[0] == server.ProfileNames[0] {
 					reqHdr = MakeReqHdr(oldCfg.MetaData.ServerParams)
 				}
-				params, reqInf, err := toClient.GetServerProfileParameters(*server.Profile, reqHdr)
-				log.Infoln(toreq.RequestInfoStr(reqInf, "GetURLSigKeys("+*server.Profile+")"))
+				params, reqInf, err := toClient.GetServerProfileParameters(server.ProfileNames[0], reqHdr)
+				log.Infoln(toreq.RequestInfoStr(reqInf, "GetURLSigKeys("+server.ProfileNames[0]+")"))
 				if err != nil {
-					return errors.New("getting server profile '" + *server.Profile + "' parameters: " + err.Error())
+					return errors.New("getting server profile '" + server.ProfileNames[0] + "' parameters: " + err.Error())
 				} else if len(params) == 0 {
-					return errors.New("getting server profile '" + *server.Profile + "' parameters: no parameters (profile not found?)")
+					return errors.New("getting server profile '" + server.ProfileNames[0] + "' parameters: no parameters (profile not found?)")
 				}
 
 				if reqInf.StatusCode == http.StatusNotModified {
@@ -519,13 +519,13 @@ func GetConfigData(toClient *toreq.TOClient, disableProxy bool, cacheHostName st
 			defer func(start time.Time) { log.Infof("profileF took %v\n", time.Since(start)) }(time.Now())
 			{
 				reqHdr := (http.Header)(nil)
-				if oldCfg != nil && oldServer.Profile != nil && *oldServer.Profile == *server.Profile {
+				if oldCfg != nil && len(oldServer.ProfileNames) != 0 && oldServer.ProfileNames[0] == server.ProfileNames[0] {
 					reqHdr = MakeReqHdr(oldCfg.MetaData.Profile)
 				}
-				profile, reqInf, err := toClient.GetProfileByName(*server.Profile, reqHdr)
-				log.Infoln(toreq.RequestInfoStr(reqInf, "GetProfileByName("+*server.Profile+")"))
+				profile, reqInf, err := toClient.GetProfileByName(server.ProfileNames[0], reqHdr)
+				log.Infoln(toreq.RequestInfoStr(reqInf, "GetProfileByName("+server.ProfileNames[0]+")"))
 				if err != nil {
-					return errors.New("getting profile '" + *server.Profile + "': " + err.Error())
+					return errors.New("getting profile '" + server.ProfileNames[0] + "': " + err.Error())
 				}
 				if reqInf.StatusCode == http.StatusNotModified {
 					log.Infof("Getting config: %v not modified, using old config", "Profile")
diff --git a/cache-config/t3cutil/toreq/toreqold/conversions.go b/cache-config/t3cutil/toreq/toreqold/conversions.go
index 79a37bc6db..a44c58b3b9 100644
--- a/cache-config/t3cutil/toreq/toreqold/conversions.go
+++ b/cache-config/t3cutil/toreq/toreqold/conversions.go
@@ -41,7 +41,7 @@ func serversToLatest(svs tc.ServersV3Response) ([]atscfg.Server, error) {
 // serverToLatest converts a tc.Server to tc.ServerV30.
 // This is necessary, because the old Traffic Ops client doesn't return the same type as the latest client.
 func serverToLatest(oldSv *tc.ServerV30) (*atscfg.Server, error) {
-	sv, err := oldSv.UpgradeToV40()
+	sv, err := oldSv.UpgradeToV40([]string{*oldSv.Profile})
 	if err != nil {
 		return nil, err
 	}
diff --git a/dev/traffic_ops/seed.psql b/dev/traffic_ops/seed.psql
index 7fb5faaf73..5f30def8e8 100644
--- a/dev/traffic_ops/seed.psql
+++ b/dev/traffic_ops/seed.psql
@@ -243,6 +243,11 @@ INSERT INTO "server" (
 )
 ON CONFLICT DO NOTHING;
 
+INSERT into server_profile(server, profile_name, priority)
+SELECT s.id, p.name, 0
+FROM server AS s
+    JOIN profile p ON p.id=s.profile;
+
 INSERT INTO interface (
 	monitor,
 	"name",
diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst
index 548bdb80bd..92bdbb6020 100644
--- a/docs/source/api/v4/servers.rst
+++ b/docs/source/api/v4/servers.rst
@@ -137,9 +137,7 @@ Response Structure
 :offlineReason:   A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status
 :physLocation:    The name of the physical location where the server resides
 :physLocationId:  An integral, unique identifier for the physical location where the server resides
-:profile:         The :ref:`profile-name` of the :term:`Profile` used by this server
-:profileDesc:     A :ref:`profile-description` of the :term:`Profile` used by this server
-:profileId:       The :ref:`profile-id` the :term:`Profile` used by this server
+:profileNames:    List of :ref:`profile-name` of the :term:`Profiles` used by this server
 :revalPending:    A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation
 :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch
 :revalApplyTime:  The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch
@@ -198,9 +196,7 @@ Response Structure
 		"offlineReason": "",
 		"physLocation": "Apachecon North America 2018",
 		"physLocationId": 1,
-		"profile": "ATS_MID_TIER_CACHE",
-		"profileDesc": "Mid Cache - Apache Traffic Server",
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"rack": "",
 		"revalPending": false,
 		"revalUpdateTime": "1969-12-31T17:00:00-07:00",
@@ -294,7 +290,7 @@ Request Structure
 		This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server.
 
 :physLocationId: An integral, unique identifier for the physical location where the server resides
-:profileId:      The :ref:`profile-id` the :term:`Profile` that shall be used by this server
+:profileNames:   List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server
 :rack:           An optional string indicating "server rack" location
 :statusId:       The integral, unique identifier of the status of this server
 
@@ -364,7 +360,7 @@ Request Structure
 		"mgmtIpNetmask": "",
 		"offlineReason": "",
 		"physLocationId": 1,
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"statusId": 3,
 		"tcpPort": 80,
 		"typeId": 12
@@ -428,9 +424,7 @@ Response Structure
 :offlineReason:   A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status
 :physLocation:    The name of the :term:`Physical Location` where the server resides
 :physLocationId:  An integral, unique identifier for the :term:`Physical Location` where the server resides
-:profile:         The :ref:`profile-name` of the :term:`Profile` used by this server
-:profileDesc:     A :ref:`profile-description` of the :term:`Profile` used by this server
-:profileId:       The :ref:`profile-id` the :term:`Profile` used by this server
+:profileNames:    List of :ref:`profile-name` of the :term:`Profiles` used by this server
 :revalPending:    A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation
 :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch
 :revalApplyTime:  The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch
@@ -495,9 +489,7 @@ Response Structure
 		"offlineReason": "",
 		"physLocation": "Apachecon North America 2018",
 		"physLocationId": 1,
-		"profile": "ATS_MID_TIER_CACHE",
-		"profileDesc": "Mid Cache - Apache Traffic Server",
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"rack": null,
 		"revalPending": false,
 		"revalUpdateTime": "1969-12-31T17:00:00-07:00",
diff --git a/docs/source/api/v4/servers_id.rst b/docs/source/api/v4/servers_id.rst
index 62176fbdda..e2a005b6ff 100644
--- a/docs/source/api/v4/servers_id.rst
+++ b/docs/source/api/v4/servers_id.rst
@@ -82,7 +82,7 @@ Request Structure
 		This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server.
 
 :physLocationId:  An integral, unique identifier for the physical location where the server resides
-:profileId:       The :ref:`profile-id` the :term:`Profile` that shall be used by this server
+:profileNames:    List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server
 :rack:            An optional string indicating "server rack" location
 :statusId:        The integral, unique identifier of the status of this server
 
@@ -152,7 +152,7 @@ Request Structure
 		"mgmtIpNetmask": "",
 		"offlineReason": "",
 		"physLocationId": 1,
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"statusId": 3,
 		"tcpPort": 80,
 		"typeId": 12
@@ -216,9 +216,7 @@ Response Structure
 :offlineReason:   A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status
 :physLocation:    The name of the :term:`Physical Location` where the server resides
 :physLocationId:  An integral, unique identifier for the :term:`Physical Location` where the server resides
-:profile:         The :ref:`profile-name` of the :term:`Profile` used by this server
-:profileDesc:     A :ref:`profile-description` of the :term:`Profile` used by this server
-:profileId:       The :ref:`profile-id` the :term:`Profile` used by this server
+:profileNames:    List of :ref:`profile-name` of the :term:`Profiles` used by this server
 :revalPending:    A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation
 :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch
 :revalApplyTime:  The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch
@@ -287,9 +285,7 @@ Response Structure
 		"offlineReason": "",
 		"physLocation": "Apachecon North America 2018",
 		"physLocationId": 1,
-		"profile": "ATS_MID_TIER_CACHE",
-		"profileDesc": "Mid Cache - Apache Traffic Server",
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"rack": null,
 		"revalPending": false,
 		"revalUpdateTime": "1969-12-31T17:00:00-07:00",
@@ -415,9 +411,7 @@ Response Structure
 :offlineReason:   A user-entered reason why the server was in ADMIN_DOWN or OFFLINE status
 :physLocation:    The name of the physical location where the server resided
 :physLocationId:  An integral, unique identifier for the physical location where the server resided
-:profile:         The :ref:`profile-name` of the :term:`Profile` which was used by this server
-:profileDesc:     A :ref:`profile-description` of the :term:`Profile` which was used by this server
-:profileId:       The :ref:`profile-id` the :term:`Profile` which was used by this server
+:profileNames:    List of :ref:`profile-name` of the :term:`Profiles` which was used by this server
 :revalPending:    A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation
 :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch
 :revalApplyTime:  The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch
@@ -482,9 +476,7 @@ Response Structure
 		"offlineReason": "",
 		"physLocation": "Apachecon North America 2018",
 		"physLocationId": 1,
-		"profile": "ATS_MID_TIER_CACHE",
-		"profileDesc": "Mid Cache - Apache Traffic Server",
-		"profileId": 10,
+		"profileNames": ["ATS_MID_TIER_CACHE"],
 		"rack": null,
 		"revalPending": false,
 		"revalUpdateTime": "1969-12-31T17:00:00-07:00",
diff --git a/infrastructure/cdn-in-a-box/enroller/server_template.json b/infrastructure/cdn-in-a-box/enroller/server_template.json
index b51ed04532..f31a5c124e 100644
--- a/infrastructure/cdn-in-a-box/enroller/server_template.json
+++ b/infrastructure/cdn-in-a-box/enroller/server_template.json
@@ -12,7 +12,7 @@
   ],
   "type": "$MY_TYPE",
   "physLocation": "Apachecon North America 2018",
-  "profile": "$MY_PROFILE",
+  "profileNames": ["$MY_PROFILE"],
   "cdnName": "$MY_CDN",
   "updPending": false,
   "status": "$MY_STATUS",
diff --git a/lib/go-atscfg/astatsdotconfig.go b/lib/go-atscfg/astatsdotconfig.go
index 9dc09c7f5c..ee142eaf2e 100644
--- a/lib/go-atscfg/astatsdotconfig.go
+++ b/lib/go-atscfg/astatsdotconfig.go
@@ -47,7 +47,7 @@ func MakeAStatsDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "server missing Profile")
 	}
 
diff --git a/lib/go-atscfg/atscfg_test.go b/lib/go-atscfg/atscfg_test.go
index 03050ec3e0..c582ebf70a 100644
--- a/lib/go-atscfg/atscfg_test.go
+++ b/lib/go-atscfg/atscfg_test.go
@@ -145,7 +145,6 @@ func setIPInfo(sv *Server, interfaceName string, ipAddress string, ip6Address st
 
 func makeGenericServer() *Server {
 	server := &Server{}
-	server.ProfileID = util.IntPtr(42)
 	server.CDNName = util.StrPtr("myCDN")
 	server.Cachegroup = util.StrPtr("cg0")
 	server.CachegroupID = util.IntPtr(422)
@@ -155,8 +154,7 @@ func makeGenericServer() *Server {
 	server.HTTPSPort = util.IntPtr(12443)
 	server.ID = util.IntPtr(44)
 	setIP(server, "192.168.2.1")
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("serverprofile")
+	server.ProfileNames = []string{"serverprofile"}
 	server.TCPPort = util.IntPtr(80)
 	server.Type = "EDGE"
 	server.TypeID = util.IntPtr(91)
diff --git a/lib/go-atscfg/atsdotrules.go b/lib/go-atscfg/atsdotrules.go
index ea80de4fe1..946fea2cb1 100644
--- a/lib/go-atscfg/atsdotrules.go
+++ b/lib/go-atscfg/atsdotrules.go
@@ -46,7 +46,7 @@ func MakeATSDotRules(
 		opt = &ATSDotRulesOpts{}
 	}
 	warnings := []string{}
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "server missing Profile")
 	}
 
diff --git a/lib/go-atscfg/atsdotrules_test.go b/lib/go-atscfg/atsdotrules_test.go
index 7bf7c1ddeb..a64bd1c668 100644
--- a/lib/go-atscfg/atsdotrules_test.go
+++ b/lib/go-atscfg/atsdotrules_test.go
@@ -29,7 +29,7 @@ import (
 func TestMakeATSDotRules(t *testing.T) {
 	server := makeGenericServer()
 	serverProfile := "myProfile"
-	server.Profile = &serverProfile
+	server.ProfileNames = []string{serverProfile}
 
 	hdr := "myHeaderComment"
 
diff --git a/lib/go-atscfg/cachedotconfig.go b/lib/go-atscfg/cachedotconfig.go
index a98da6775b..cb50e6d983 100644
--- a/lib/go-atscfg/cachedotconfig.go
+++ b/lib/go-atscfg/cachedotconfig.go
@@ -67,13 +67,13 @@ func makeCacheDotConfigEdge(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server missing profile")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing profiles")
 	}
 
 	profileServerIDsMap := map[int]struct{}{}
 	for _, sv := range servers {
-		if sv.Profile == nil {
+		if len(sv.ProfileNames) == 0 {
 			warnings = append(warnings, "servers had server with nil profile, skipping!")
 			continue
 		}
@@ -81,7 +81,16 @@ func makeCacheDotConfigEdge(
 			warnings = append(warnings, "servers had server with nil id, skipping!")
 			continue
 		}
-		if *sv.Profile != *server.Profile {
+		if len(sv.ProfileNames) != len(server.ProfileNames) {
+			continue
+		}
+		profilesTheSame := true
+		for i, _ := range server.ProfileNames {
+			if sv.ProfileNames[i] != server.ProfileNames[i] {
+				profilesTheSame = false
+			}
+		}
+		if !profilesTheSame {
 			continue
 		}
 		profileServerIDsMap[*sv.ID] = struct{}{}
diff --git a/lib/go-atscfg/cachedotconfig_test.go b/lib/go-atscfg/cachedotconfig_test.go
index 5e459478e8..d9e64ebb37 100644
--- a/lib/go-atscfg/cachedotconfig_test.go
+++ b/lib/go-atscfg/cachedotconfig_test.go
@@ -30,7 +30,7 @@ import (
 func TestMakeCacheDotConfig(t *testing.T) {
 	server := makeGenericServer()
 	serverProfile := "myProfile"
-	server.Profile = &serverProfile
+	server.ProfileNames = []string{serverProfile}
 	servers := []Server{*server}
 
 	ds0 := makeGenericDS()
diff --git a/lib/go-atscfg/dropqstringdotconfig.go b/lib/go-atscfg/dropqstringdotconfig.go
index c8d8d9c84b..164d0fba9c 100644
--- a/lib/go-atscfg/dropqstringdotconfig.go
+++ b/lib/go-atscfg/dropqstringdotconfig.go
@@ -46,7 +46,7 @@ func MakeDropQStringDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "this server missing Profile")
 	}
 
diff --git a/lib/go-atscfg/dropqstringdotconfig_test.go b/lib/go-atscfg/dropqstringdotconfig_test.go
index 9c0f012651..9441bf5b55 100644
--- a/lib/go-atscfg/dropqstringdotconfig_test.go
+++ b/lib/go-atscfg/dropqstringdotconfig_test.go
@@ -31,7 +31,7 @@ func TestMakeDropQStringDotConfig(t *testing.T) {
 	profileName := "myProfile"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	params := []tc.Parameter{
 		{
diff --git a/lib/go-atscfg/facts.go b/lib/go-atscfg/facts.go
index 750e23a64f..63c6459890 100644
--- a/lib/go-atscfg/facts.go
+++ b/lib/go-atscfg/facts.go
@@ -19,6 +19,8 @@ package atscfg
  * under the License.
  */
 
+import "strings"
+
 const ContentType12MFacts = ContentTypeTextASCII
 const LineComment12MFacts = LineCommentHash
 
@@ -39,13 +41,13 @@ func Make12MFacts(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "this server missing Profile")
 	}
 
 	hdr := makeHdrComment(opt.HdrComment)
 	txt := hdr
-	txt += "profile:" + *server.Profile + "\n"
+	txt += "profiles:" + strings.Join(server.ProfileNames, ", ") + "\n"
 
 	return Cfg{
 		Text:        txt,
diff --git a/lib/go-atscfg/facts_test.go b/lib/go-atscfg/facts_test.go
index 8af1e0d8c3..ab93be1db7 100644
--- a/lib/go-atscfg/facts_test.go
+++ b/lib/go-atscfg/facts_test.go
@@ -27,7 +27,7 @@ import (
 func TestMake12MFacts(t *testing.T) {
 	server := makeGenericServer()
 	profileName := "myProfile"
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	hdr := "myHeaderComment"
 
diff --git a/lib/go-atscfg/hostingdotconfig_test.go b/lib/go-atscfg/hostingdotconfig_test.go
index c58b4cf053..6b3ba99d23 100644
--- a/lib/go-atscfg/hostingdotconfig_test.go
+++ b/lib/go-atscfg/hostingdotconfig_test.go
@@ -33,8 +33,7 @@ func TestMakeHostingDotConfig(t *testing.T) {
 	server := makeGenericServer()
 	server.HostName = util.StrPtr("server0")
 	server.CDNName = &cdnName
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("serverprofile")
+	server.ProfileNames = []string{"serverprofile"}
 	hdr := "myHeaderComment"
 
 	serverParams := []tc.Parameter{
@@ -42,19 +41,19 @@ func TestMakeHostingDotConfig(t *testing.T) {
 			Name:       ParamRAMDrivePrefix,
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "ParamRAMDrivePrefix-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 		tc.Parameter{
 			Name:       ParamDrivePrefix,
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "ParamDrivePrefix-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 		tc.Parameter{
 			Name:       "somethingelse",
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "somethingelse-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 	}
 
@@ -126,9 +125,8 @@ func TestMakeHostingDotConfigTopologiesIgnoreDSS(t *testing.T) {
 	server.Cachegroup = util.StrPtr("edgeCG")
 	server.CDNName = &cdnName
 	server.CDNID = util.IntPtr(400)
-	server.ProfileID = util.IntPtr(46)
 	server.ID = util.IntPtr(899)
-	server.Profile = util.StrPtr("serverprofile")
+	server.ProfileNames = []string{"serverprofile"}
 	hdr := "myHeaderComment"
 
 	serverParams := []tc.Parameter{
@@ -136,19 +134,19 @@ func TestMakeHostingDotConfigTopologiesIgnoreDSS(t *testing.T) {
 			Name:       ParamRAMDrivePrefix,
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "ParamRAMDrivePrefix-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 		tc.Parameter{
 			Name:       ParamDrivePrefix,
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "ParamDrivePrefix-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 		tc.Parameter{
 			Name:       "somethingelse",
 			ConfigFile: HostingConfigParamConfigFile,
 			Value:      "somethingelse-shouldnotappearinconfig",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		},
 	}
 
diff --git a/lib/go-atscfg/loggingdotconfig.go b/lib/go-atscfg/loggingdotconfig.go
index 24878c9cac..f87b1e592d 100644
--- a/lib/go-atscfg/loggingdotconfig.go
+++ b/lib/go-atscfg/loggingdotconfig.go
@@ -53,7 +53,7 @@ func MakeLoggingDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "this server missing Profile")
 	}
 
@@ -75,7 +75,7 @@ func MakeLoggingDotConfig(
 			format := paramData[logFormatField+".Format"]
 			if format == "" {
 				// TODO determine if the line should be excluded. Perl includes it anyway, without checking.
-				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.config format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n", *server.Profile, logFormatField))
+				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.config format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n", server.ProfileNames, logFormatField))
 			}
 			format = strings.Replace(format, `"`, `\"`, -1)
 			text += logFormatName + " = format {\n"
@@ -94,7 +94,7 @@ func MakeLoggingDotConfig(
 			filter := paramData[logFilterField+".Filter"]
 			if filter == "" {
 				// TODO determine if the line should be excluded. Perl includes it anyway, without checking.
-				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.config format '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n", *server.Profile, logFilterField))
+				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.config format '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n", server.ProfileNames, logFilterField))
 			}
 
 			filter = strings.Replace(filter, `\`, `\\`, -1)
diff --git a/lib/go-atscfg/loggingdotconfig_test.go b/lib/go-atscfg/loggingdotconfig_test.go
index 8c24021dae..a5100d8b9a 100644
--- a/lib/go-atscfg/loggingdotconfig_test.go
+++ b/lib/go-atscfg/loggingdotconfig_test.go
@@ -29,7 +29,7 @@ func TestMakeLoggingDotConfig(t *testing.T) {
 	hdrComment := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	params := makeParamsFromMap("serverProfile", LoggingFileName, map[string]string{
 		"LogFormat.Name":           "myFormatName",
diff --git a/lib/go-atscfg/loggingdotyaml.go b/lib/go-atscfg/loggingdotyaml.go
index 5b66b438a2..ac6e279e59 100644
--- a/lib/go-atscfg/loggingdotyaml.go
+++ b/lib/go-atscfg/loggingdotyaml.go
@@ -50,7 +50,7 @@ func MakeLoggingDotYAML(
 	warnings := []string{}
 	requiredIndent := 0
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "this server missing Profile")
 	}
 
@@ -82,7 +82,7 @@ func MakeLoggingDotYAML(
 			format := paramData[logFormatField+".Format"]
 			if format == "" {
 				// TODO determine if the line should be excluded. Perl includes it anyway, without checking.
-				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.yaml format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n", *server.Profile, logFormatField))
+				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.yaml format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n", server.ProfileNames, logFormatField))
 			}
 			text += indentSpaces + " - name: " + logFormatName + " \n"
 			text += indentSpaces + "   format: '" + format + "'\n"
@@ -99,7 +99,7 @@ func MakeLoggingDotYAML(
 			filter := paramData[logFilterField+".Filter"]
 			if filter == "" {
 				// TODO determine if the line should be excluded. Perl includes it anyway, without checking.
-				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.yaml filter '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n", *server.Profile, logFilterField))
+				warnings = append(warnings, fmt.Sprintf("profile '%v' has logging.yaml filter '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n", server.ProfileNames, logFilterField))
 			}
 			logFilterType := paramData[logFilterField+".Type"]
 			if logFilterType == "" {
diff --git a/lib/go-atscfg/loggingdotyaml_test.go b/lib/go-atscfg/loggingdotyaml_test.go
index 0a0a13287a..3cda3ef24a 100644
--- a/lib/go-atscfg/loggingdotyaml_test.go
+++ b/lib/go-atscfg/loggingdotyaml_test.go
@@ -32,7 +32,7 @@ func TestMakeLoggingDotYAML(t *testing.T) {
 	hdr := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	params := makeParamsFromMap("serverProfile", LoggingYAMLFileName, map[string]string{
 		"LogFormat.Name":           "myFormatName",
@@ -101,7 +101,7 @@ func TestMakeLoggingDotYAMLMultiFormat(t *testing.T) {
 	})
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	cfg, err := MakeLoggingDotYAML(server, paramData, &LoggingDotYAMLOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/logsdotxml.go b/lib/go-atscfg/logsdotxml.go
index a493821987..237886b72b 100644
--- a/lib/go-atscfg/logsdotxml.go
+++ b/lib/go-atscfg/logsdotxml.go
@@ -49,7 +49,7 @@ func MakeLogsXMLDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
+	if len(server.ProfileNames) == 0 {
 		return Cfg{}, makeErr(warnings, "this server missing Profile")
 	}
 
diff --git a/lib/go-atscfg/logsdotxml_test.go b/lib/go-atscfg/logsdotxml_test.go
index 8bde2e6fdb..ae2a04b270 100644
--- a/lib/go-atscfg/logsdotxml_test.go
+++ b/lib/go-atscfg/logsdotxml_test.go
@@ -37,7 +37,7 @@ func TestMakeLogsXMLDotConfig(t *testing.T) {
 	})
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	cfg, err := MakeLogsXMLDotConfig(server, paramData, &LogsXMLDotConfigOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/meta.go b/lib/go-atscfg/meta.go
index cafdc9ed94..537b1bafba 100644
--- a/lib/go-atscfg/meta.go
+++ b/lib/go-atscfg/meta.go
@@ -57,8 +57,6 @@ func MakeConfigFilesList(
 		return nil, warnings, errors.New("this server missing Cachegroup")
 	} else if server.CachegroupID == nil {
 		return nil, warnings, errors.New("this server missing CachegroupID")
-	} else if server.ProfileID == nil {
-		return nil, warnings, errors.New("server missing ProfileID")
 	} else if server.TCPPort == nil {
 		return nil, warnings, errors.New("server missing TCPPort")
 	} else if server.HostName == nil {
@@ -69,7 +67,7 @@ func MakeConfigFilesList(
 		return nil, warnings, errors.New("server missing CDNName")
 	} else if server.ID == nil {
 		return nil, warnings, errors.New("server missing ID")
-	} else if server.Profile == nil {
+	} else if len(server.ProfileNames) == 0 {
 		return nil, warnings, errors.New("server missing Profile")
 	}
 
diff --git a/lib/go-atscfg/meta_test.go b/lib/go-atscfg/meta_test.go
index 45355bb8db..64710d7f82 100644
--- a/lib/go-atscfg/meta_test.go
+++ b/lib/go-atscfg/meta_test.go
@@ -41,8 +41,8 @@ func TestMakeMetaConfig(t *testing.T) {
 	setIP(server, ip)
 	// server.ParentCacheGroupID=            45
 	// server.ParentCacheGroupType=          "MID_LOC"
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("myserverprofile")
+	//server.ProfileID = util.IntPtr(46)
+	server.ProfileNames = []string{"myserverprofile"}
 	server.TCPPort = util.IntPtr(80)
 	// server.SecondaryParentCacheGroupID=   47
 	// server.SecondaryParentCacheGroupType= "MID_LOC"
@@ -65,7 +65,7 @@ func TestMakeMetaConfig(t *testing.T) {
 			Name:       "location",
 			ConfigFile: name,
 			Value:      "/my/location/",
-			Profiles:   []byte(`["` + *server.Profile + `"]`),
+			Profiles:   []byte(`["` + server.ProfileNames[0] + `"]`),
 		}
 	}
 
diff --git a/lib/go-atscfg/parentdotconfig.go b/lib/go-atscfg/parentdotconfig.go
index 74341117e8..9adc67a6d5 100644
--- a/lib/go-atscfg/parentdotconfig.go
+++ b/lib/go-atscfg/parentdotconfig.go
@@ -162,7 +162,7 @@ func makeParentDotConfigData(
 		return nil, warnings, errors.New("server CDNName missing")
 	} else if server.Cachegroup == nil || *server.Cachegroup == "" {
 		return nil, warnings, errors.New("server Cachegroup missing")
-	} else if server.Profile == nil || *server.Profile == "" {
+	} else if len(server.ProfileNames) == 0 {
 		return nil, warnings, errors.New("server Profile missing")
 	} else if server.TCPPort == nil {
 		return nil, warnings, errors.New("server TCPPort missing")
@@ -1216,7 +1216,7 @@ func serverParentageParams(sv *Server, params []parameterWithProfilesMap) (profi
 		profileCache.Port = *sv.TCPPort
 	}
 	for _, param := range params {
-		if _, ok := param.ProfileNames[*sv.Profile]; !ok {
+		if _, ok := param.ProfileNames[(sv.ProfileNames)[0]]; !ok {
 			continue
 		}
 		switch param.Name {
@@ -1694,8 +1694,8 @@ func getOriginServersAndProfileCaches(
 		} else if cgSv.TypeID == nil {
 			warnings = append(warnings, "getting origin servers: got server with nil TypeID, skipping!")
 			continue
-		} else if cgSv.ProfileID == nil {
-			warnings = append(warnings, "getting origin servers: got server with nil ProfileID, skipping!")
+		} else if len(cgSv.ProfileNames) == 0 {
+			warnings = append(warnings, "getting origin servers: got server with no profile names, skipping!")
 			continue
 		} else if cgSv.CDNID == nil {
 			warnings = append(warnings, "getting origin servers: got server with nil CDNID, skipping!")
@@ -1719,7 +1719,6 @@ func getOriginServersAndProfileCaches(
 			CacheGroupID: *cgSv.CachegroupID,
 			Status:       *cgSv.StatusID,
 			Type:         *cgSv.TypeID,
-			ProfileID:    ProfileID(*cgSv.ProfileID),
 			CDN:          *cgSv.CDNID,
 			TypeName:     cgSv.Type,
 			Domain:       *cgSv.DomainName,
@@ -1741,8 +1740,8 @@ func getOriginServersAndProfileCaches(
 		}
 
 		if _, profileCachesHasProfile := profileCaches[realCGServer.ProfileID]; !profileCachesHasProfile {
-			if profileCache, profileParamsHasProfile := profileParams[*cgSv.Profile]; !profileParamsHasProfile {
-				warnings = append(warnings, fmt.Sprintf("cachegroup has server with profile %+v but that profile has no parameters\n", *cgSv.ProfileID))
+			if profileCache, profileParamsHasProfile := profileParams[cgSv.ProfileNames[0]]; !profileParamsHasProfile {
+				warnings = append(warnings, fmt.Sprintf("cachegroup has server with profile %+v but that profile has no parameters\n", cgSv.ProfileNames[0]))
 				profileCaches[realCGServer.ProfileID] = defaultProfileCache()
 			} else {
 				profileCaches[realCGServer.ProfileID] = profileCache
@@ -1761,17 +1760,17 @@ func getParentConfigProfileParams(
 	warnings := []string{}
 	parentConfigServerCacheProfileParams := map[string]profileCache{} // map[profileName]ProfileCache
 	for _, cgServer := range cgServers {
-		if cgServer.Profile == nil {
-			warnings = append(warnings, "getting parent config profile params: server has nil profile, skipping!")
+		if len(cgServer.ProfileNames) == 0 {
+			warnings = append(warnings, "getting parent config profile params: server has nil profiles, skipping!")
 			continue
 		}
-		profileCache, ok := parentConfigServerCacheProfileParams[*cgServer.Profile]
+		profileCache, ok := parentConfigServerCacheProfileParams[cgServer.ProfileNames[0]]
 		if !ok {
 			profileCache = defaultProfileCache()
 		}
-		params, ok := profileParentConfigParams[*cgServer.Profile]
+		params, ok := profileParentConfigParams[cgServer.ProfileNames[0]]
 		if !ok {
-			parentConfigServerCacheProfileParams[*cgServer.Profile] = profileCache
+			parentConfigServerCacheProfileParams[cgServer.ProfileNames[0]] = profileCache
 			continue
 		}
 		for name, val := range params {
@@ -1803,7 +1802,7 @@ func getParentConfigProfileParams(
 				profileCache.NotAParent = val != "false"
 			}
 		}
-		parentConfigServerCacheProfileParams[*cgServer.Profile] = profileCache
+		parentConfigServerCacheProfileParams[cgServer.ProfileNames[0]] = profileCache
 	}
 	return parentConfigServerCacheProfileParams, warnings
 }
@@ -1922,8 +1921,8 @@ func getProfileParentConfigParams(tcParentConfigParams []tc.Parameter) (map[stri
 func getServerParentConfigParams(server *Server, profileParentConfigParams map[string]map[string]string) map[string]string {
 	// We only need parent.config params, don't need all the params on the server
 	serverParams := map[string]string{}
-	if server.Profile == nil || *server.Profile != "" { // TODO warn/error if false? Servers requires profiles
-		for name, val := range profileParentConfigParams[*server.Profile] {
+	if len(server.ProfileNames) != 0 || server.ProfileNames[0] != "" { // TODO warn/error if false? Servers requires profiles
+		for name, val := range profileParentConfigParams[server.ProfileNames[0]] {
 			if name == ParentConfigParamQStringHandling ||
 				name == ParentConfigParamAlgorithm ||
 				name == ParentConfigParamQString {
diff --git a/lib/go-atscfg/parentdotconfig_test.go b/lib/go-atscfg/parentdotconfig_test.go
index 3982bcd678..628b973cbd 100644
--- a/lib/go-atscfg/parentdotconfig_test.go
+++ b/lib/go-atscfg/parentdotconfig_test.go
@@ -3152,7 +3152,6 @@ func warningsContains(warnings []string, str string) bool {
 
 func makeTestParentServer() *Server {
 	server := &Server{}
-	server.ProfileID = util.IntPtr(42)
 	server.CDNName = util.StrPtr("myCDN")
 	server.Cachegroup = util.StrPtr("cg0")
 	server.CachegroupID = util.IntPtr(422)
@@ -3162,8 +3161,7 @@ func makeTestParentServer() *Server {
 	server.HTTPSPort = util.IntPtr(12443)
 	server.ID = util.IntPtr(44)
 	setIP(server, "192.168.2.1")
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("serverprofile")
+	server.ProfileNames = []string{"serverprofile"}
 	server.TCPPort = util.IntPtr(80)
 	server.Type = "EDGE"
 	server.TypeID = util.IntPtr(91)
diff --git a/lib/go-atscfg/plugindotconfig.go b/lib/go-atscfg/plugindotconfig.go
index 1d2d43d2f3..acf01fd132 100644
--- a/lib/go-atscfg/plugindotconfig.go
+++ b/lib/go-atscfg/plugindotconfig.go
@@ -45,8 +45,8 @@ func MakePluginDotConfig(
 		opt = &PluginDotConfigOpts{}
 	}
 	warnings := []string{}
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server profile missing")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing profiles")
 	}
 
 	paramData, paramWarns := paramsToMap(filterParams(serverParams, PluginFileName, "", "", "location"))
diff --git a/lib/go-atscfg/plugindotconfig_test.go b/lib/go-atscfg/plugindotconfig_test.go
index 78aa1256ed..866be41421 100644
--- a/lib/go-atscfg/plugindotconfig_test.go
+++ b/lib/go-atscfg/plugindotconfig_test.go
@@ -35,7 +35,7 @@ func TestMakePluginDotConfig(t *testing.T) {
 	})
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	cfg, err := MakePluginDotConfig(server, paramData, &PluginDotConfigOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/recordsdotconfig.go b/lib/go-atscfg/recordsdotconfig.go
index 0e708029ff..997003e1b3 100644
--- a/lib/go-atscfg/recordsdotconfig.go
+++ b/lib/go-atscfg/recordsdotconfig.go
@@ -66,8 +66,8 @@ func MakeRecordsDotConfig(
 		opt = &RecordsConfigOpts{}
 	}
 	warnings := []string{}
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server profile missing")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing profiles")
 	}
 
 	params, paramWarns := paramsToMap(filterParams(serverParams, RecordsFileName, "", "", "location"))
diff --git a/lib/go-atscfg/recordsdotconfig_test.go b/lib/go-atscfg/recordsdotconfig_test.go
index e0c8cc9302..e2ab3c62ba 100644
--- a/lib/go-atscfg/recordsdotconfig_test.go
+++ b/lib/go-atscfg/recordsdotconfig_test.go
@@ -22,8 +22,6 @@ package atscfg
 import (
 	"strings"
 	"testing"
-
-	"github.com/apache/trafficcontrol/lib/go-util"
 )
 
 func TestMakeRecordsDotConfig(t *testing.T) {
@@ -45,7 +43,7 @@ func TestMakeRecordsDotConfig(t *testing.T) {
 	ip6Str := "2001:db8::9"
 	ip6CIDR := ip6Str + "/48" // set the ip to a cidr, to make sure addr logic removes it
 	setIP6(server, ip6CIDR)
-	server.Profile = util.StrPtr(profileName)
+	server.ProfileNames = []string{profileName}
 	opt := &RecordsConfigOpts{}
 	opt.DNSLocalBindServiceAddr = true
 	opt.HdrComment = hdr
@@ -184,7 +182,7 @@ func TestMakeRecordsDotConfigDNSLocalBindNoOverrideV4(t *testing.T) {
 	ip6Str := "2001:db8::9"
 	ip6CIDR := ip6Str + "/48" // set the ip to a cidr, to make sure addr logic removes it
 	setIP6(server, ip6CIDR)
-	server.Profile = util.StrPtr(profileName)
+	server.ProfileNames = []string{profileName}
 	opt := &RecordsConfigOpts{}
 	opt.DNSLocalBindServiceAddr = true
 	opt.HdrComment = hdr
@@ -227,7 +225,7 @@ func TestMakeRecordsDotConfigDNSLocalBindNoOverrideV6(t *testing.T) {
 	ip6Str := "2001:db8::9"
 	ip6CIDR := ip6Str + "/48" // set the ip to a cidr, to make sure addr logic removes it
 	setIP6(server, ip6CIDR)
-	server.Profile = util.StrPtr(profileName)
+	server.ProfileNames = []string{profileName}
 	opt := &RecordsConfigOpts{}
 	opt.HdrComment = hdr
 	opt.DNSLocalBindServiceAddr = true
@@ -271,7 +269,7 @@ func TestMakeRecordsDotConfigDNSLocalBindNoOverrideBoth(t *testing.T) {
 	ip6Str := "2001:db8::9"
 	ip6CIDR := ip6Str + "/48" // set the ip to a cidr, to make sure addr logic removes it
 	setIP6(server, ip6CIDR)
-	server.Profile = util.StrPtr(profileName)
+	server.ProfileNames = []string{profileName}
 	opt := &RecordsConfigOpts{}
 	opt.HdrComment = hdr
 	opt.DNSLocalBindServiceAddr = true
diff --git a/lib/go-atscfg/remapdotconfig_test.go b/lib/go-atscfg/remapdotconfig_test.go
index 021fd847a3..c039a5fa3a 100644
--- a/lib/go-atscfg/remapdotconfig_test.go
+++ b/lib/go-atscfg/remapdotconfig_test.go
@@ -29,7 +29,6 @@ import (
 
 func makeTestRemapServer() *Server {
 	server := &Server{}
-	server.ProfileID = util.IntPtr(42)
 	server.CDNName = util.StrPtr("mycdn")
 	server.Cachegroup = util.StrPtr("cg0")
 	server.DomainName = util.StrPtr("mydomain")
@@ -38,8 +37,7 @@ func makeTestRemapServer() *Server {
 	server.HTTPSPort = util.IntPtr(12345)
 	server.ID = util.IntPtr(44)
 	setIP(server, "192.168.2.4")
-	server.ProfileID = util.IntPtr(46)
-	server.Profile = util.StrPtr("MyProfile")
+	server.ProfileNames = []string{"MyProfile"}
 	server.TCPPort = util.IntPtr(12080)
 	server.Type = "MID"
 	return server
@@ -224,7 +222,7 @@ func TestMakeRemapDotConfigMidLiveLocalExcluded(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -325,7 +323,7 @@ func TestMakeRemapDotConfigMid(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -440,7 +438,7 @@ func TestMakeRemapDotConfigNilOrigin(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -541,7 +539,7 @@ func TestMakeRemapDotConfigEmptyOrigin(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -680,7 +678,7 @@ func TestMakeRemapDotConfigDuplicateOrigins(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -780,7 +778,7 @@ func TestMakeRemapDotConfigNilMidRewrite(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -882,7 +880,7 @@ func TestMakeRemapDotConfigMidHasNoEdgeRewrite(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -985,7 +983,7 @@ func TestMakeRemapDotConfigMidProfileCacheKey(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -1124,7 +1122,7 @@ func TestMakeRemapDotConfigMidBgFetchHandling(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -1265,7 +1263,7 @@ func TestMakeRemapDotConfigMidRangeRequestHandling(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -1406,7 +1404,7 @@ func TestMakeRemapDotConfigMidSlicePluginRangeRequestHandling(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -1548,7 +1546,7 @@ func TestMakeRemapDotConfigAnyMap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -1946,7 +1944,7 @@ func TestMakeRemapDotConfigEdgeMissingRemapData(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2051,7 +2049,7 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacement(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2173,7 +2171,7 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTP(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2295,7 +2293,7 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTPS(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2417,7 +2415,7 @@ func TestMakeRemapDotConfigEdgeHostRegexReplacementHTTPToHTTPS(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2539,7 +2537,7 @@ func TestMakeRemapDotConfigEdgeRemapUnderscoreHTTPReplace(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2657,13 +2655,13 @@ func TestMakeRemapDotConfigEdgeDSCPRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2781,13 +2779,13 @@ func TestMakeRemapDotConfigEdgeNoDSCPRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -2905,13 +2903,13 @@ func TestMakeRemapDotConfigEdgeHeaderRewrite(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3033,13 +3031,13 @@ func TestMakeRemapDotConfigEdgeHeaderRewriteEmpty(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3161,13 +3159,13 @@ func TestMakeRemapDotConfigEdgeHeaderRewriteNil(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3289,13 +3287,13 @@ func TestMakeRemapDotConfigEdgeSigningURLSig(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3423,13 +3421,13 @@ func TestMakeRemapDotConfigEdgeSigningURISigning(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3546,13 +3544,13 @@ func TestMakeRemapDotConfigEdgeSigningNone(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3669,13 +3667,13 @@ func TestMakeRemapDotConfigEdgeSigningEmpty(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3792,13 +3790,13 @@ func TestMakeRemapDotConfigEdgeSigningWrong(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -3915,13 +3913,13 @@ func TestMakeRemapDotConfigEdgeQStringDropAtEdge(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4036,13 +4034,13 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUp(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4160,13 +4158,13 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpWithCacheKeyParameter(t *testi
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4307,13 +4305,13 @@ func TestMakeRemapDotConfigEdgeQStringIgnorePassUpCacheURLParamCacheURL(t *testi
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4424,13 +4422,13 @@ func TestMakeRemapDotConfigEdgeCacheKeyParams(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4563,13 +4561,13 @@ func TestMakeRemapDotConfigEdgeRegexRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4687,13 +4685,13 @@ func TestMakeRemapDotConfigEdgeRegexRemapEmpty(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4807,13 +4805,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestNil(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -4931,13 +4929,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestDontCache(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5079,13 +5077,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestBGFetch(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5225,13 +5223,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestSlice(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5354,13 +5352,13 @@ func TestMakeRemapDotConfigMidRangeRequestSlicePparam(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5493,13 +5491,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestSlicePparam(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5646,13 +5644,13 @@ func TestMakeRemapDotConfigRawRemapRangeDirective(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5792,13 +5790,13 @@ func TestMakeRemapDotConfigRawRemapWithoutRangeDirective(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -5931,13 +5929,13 @@ func TestMakeRemapDotConfigEdgeRangeRequestCache(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6059,13 +6057,13 @@ func TestMakeRemapDotConfigEdgeFQPacingNil(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6179,13 +6177,13 @@ func TestMakeRemapDotConfigEdgeFQPacingNegative(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6299,13 +6297,13 @@ func TestMakeRemapDotConfigEdgeFQPacingZero(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6419,13 +6417,13 @@ func TestMakeRemapDotConfigEdgeFQPacingPositive(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6543,13 +6541,13 @@ func TestMakeRemapDotConfigEdgeDNS(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6663,13 +6661,13 @@ func TestMakeRemapDotConfigEdgeDNSNoRoutingName(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6773,13 +6771,13 @@ func TestMakeRemapDotConfigEdgeRegexTypeNil(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -6888,13 +6886,13 @@ func TestMakeRemapDotConfigNoHeaderRewrite(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7008,13 +7006,13 @@ func TestMakeRemapDotConfigMidNoHeaderRewrite(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7128,13 +7126,13 @@ func TestMakeRemapDotConfigMidNoNoCacheRemapLine(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7244,13 +7242,13 @@ func TestMakeRemapDotConfigEdgeHTTPSOriginHTTPRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7376,13 +7374,13 @@ func TestMakeRemapDotConfigMidHTTPSOriginHTTPRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7498,13 +7496,13 @@ func TestMakeRemapDotConfigEdgeHTTPSOriginHTTPRemapTopology(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7683,13 +7681,13 @@ func TestMakeRemapDotConfigMidHTTPSOriginHTTPRemapTopology(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -7862,13 +7860,13 @@ func TestMakeRemapDotConfigMidLastRawRemap(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -8075,13 +8073,13 @@ func TestMakeRemapDotConfigStrategies(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -8265,13 +8263,13 @@ func TestMakeRemapDotConfigStrategiesFalseButCoreUnused(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
@@ -8463,13 +8461,13 @@ func TestMakeRemapDotConfigMidCacheParentHTTPSOrigin(t *testing.T) {
 			Name:       "serverpkgval",
 			ConfigFile: "package",
 			Value:      "serverpkgval __HOSTNAME__ foo",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 		tc.Parameter{
 			Name:       "dscp_remap_no",
 			ConfigFile: "package",
 			Value:      "notused",
-			Profiles:   []byte(*server.Profile),
+			Profiles:   []byte(server.ProfileNames[0]),
 		},
 	}
 
diff --git a/lib/go-atscfg/serverunknown_test.go b/lib/go-atscfg/serverunknown_test.go
index bb3df804df..3751e43129 100644
--- a/lib/go-atscfg/serverunknown_test.go
+++ b/lib/go-atscfg/serverunknown_test.go
@@ -31,12 +31,12 @@ func TestMakeServerUnknown(t *testing.T) {
 
 	server := makeGenericServer()
 	server.HostName = util.StrPtr("server0")
-	server.Profile = util.StrPtr("serverProfile")
+	server.ProfileNames = []string{"serverProfile"}
 	server.DomainName = util.StrPtr("example.test")
 
 	fileName := "myconfig.config"
 
-	params := makeParamsFromMapArr(*server.Profile, fileName, map[string][]string{
+	params := makeParamsFromMapArr(server.ProfileNames[0], fileName, map[string][]string{
 		"location":   []string{"locationshouldnotexist"},
 		"param0name": []string{"param0val0", "param0val1"},
 		"param1name": []string{"param1val0"},
diff --git a/lib/go-atscfg/sslservernamedotyaml.go b/lib/go-atscfg/sslservernamedotyaml.go
index 06d76cf33f..43d0856eaa 100644
--- a/lib/go-atscfg/sslservernamedotyaml.go
+++ b/lib/go-atscfg/sslservernamedotyaml.go
@@ -210,8 +210,8 @@ func GetServerSSLData(
 ) ([]SSLData, []string, error) {
 	warnings := []string{}
 
-	if server.Profile == nil {
-		return nil, warnings, errors.New("this server missing Profile")
+	if len(server.ProfileNames) == 0 {
+		return nil, warnings, errors.New("this server missing Profiles")
 	}
 
 	dsRegexes := makeDSRegexMap(dsRegexArr)
diff --git a/lib/go-atscfg/storagedotconfig.go b/lib/go-atscfg/storagedotconfig.go
index 642e6609ab..1dd5038ef6 100644
--- a/lib/go-atscfg/storagedotconfig.go
+++ b/lib/go-atscfg/storagedotconfig.go
@@ -51,8 +51,8 @@ func MakeStorageDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server missing Profile")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing Profiles")
 	}
 
 	paramData, paramWarns := paramsToMap(filterParams(serverParams, StorageFileName, "", "", "location"))
@@ -64,7 +64,7 @@ func MakeStorageDotConfig(
 	if drivePrefix := paramData["Drive_Prefix"]; drivePrefix != "" {
 		driveLetters := strings.TrimSpace(paramData["Drive_Letters"])
 		if driveLetters == "" {
-			warnings = append(warnings, fmt.Sprintf("profile %+v has Drive_Prefix parameter, but no Drive_Letters; creating anyway", *server.Profile))
+			warnings = append(warnings, fmt.Sprintf("profile %+v has Drive_Prefix parameter, but no Drive_Letters; creating anyway", server.ProfileNames[0]))
 		}
 		text += makeStorageVolumeText(drivePrefix, driveLetters, nextVolume)
 		nextVolume++
@@ -73,7 +73,7 @@ func MakeStorageDotConfig(
 	if ramDrivePrefix := paramData["RAM_Drive_Prefix"]; ramDrivePrefix != "" {
 		ramDriveLetters := strings.TrimSpace(paramData["RAM_Drive_Letters"])
 		if ramDriveLetters == "" {
-			warnings = append(warnings, fmt.Sprintf("profile %+v has RAM_Drive_Prefix parameter, but no RAM_Drive_Letters; creating anyway", *server.Profile))
+			warnings = append(warnings, fmt.Sprintf("profile %+v has RAM_Drive_Prefix parameter, but no RAM_Drive_Letters; creating anyway", server.ProfileNames[0]))
 		}
 		text += makeStorageVolumeText(ramDrivePrefix, ramDriveLetters, nextVolume)
 		nextVolume++
@@ -82,7 +82,7 @@ func MakeStorageDotConfig(
 	if ssdDrivePrefix := paramData["SSD_Drive_Prefix"]; ssdDrivePrefix != "" {
 		ssdDriveLetters := strings.TrimSpace(paramData["SSD_Drive_Letters"])
 		if ssdDriveLetters == "" {
-			warnings = append(warnings, fmt.Sprintf("profile %+v has SSD_Drive_Prefix parameter, but no SSD_Drive_Letters; creating anyway", *server.Profile))
+			warnings = append(warnings, fmt.Sprintf("profile %+v has SSD_Drive_Prefix parameter, but no SSD_Drive_Letters; creating anyway", server.ProfileNames[0]))
 		}
 		text += makeStorageVolumeText(ssdDrivePrefix, ssdDriveLetters, nextVolume)
 		nextVolume++
diff --git a/lib/go-atscfg/storagedotconfig_test.go b/lib/go-atscfg/storagedotconfig_test.go
index 3e98cb3619..f3a7dc8f39 100644
--- a/lib/go-atscfg/storagedotconfig_test.go
+++ b/lib/go-atscfg/storagedotconfig_test.go
@@ -29,7 +29,7 @@ func TestMakeStorageDotConfig(t *testing.T) {
 	hdr := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	paramData := map[string]string{
 		"Drive_Prefix":      "/dev/sd",
@@ -40,7 +40,7 @@ func TestMakeStorageDotConfig(t *testing.T) {
 		"SSD_Drive_Letters": "i,j,k",
 	}
 
-	params := makeParamsFromMap(*server.Profile, StorageFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], StorageFileName, paramData)
 
 	/*
 	   # DO NOT EDIT - Generated for myProfile by myToolName (https://myto.example.net) on Thu
@@ -94,11 +94,11 @@ func TestMakeStorageDotConfigNoParams(t *testing.T) {
 	hdr := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	paramData := map[string]string{}
 
-	params := makeParamsFromMap(*server.Profile, StorageFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], StorageFileName, paramData)
 
 	cfg, err := MakeStorageDotConfig(server, params, &StorageDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -131,7 +131,7 @@ func TestMakeStorageDotConfigNoDriveLetters(t *testing.T) {
 	hdr := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	paramData := map[string]string{
 		"Drive_Prefix":     "/dev/sd",
@@ -139,7 +139,7 @@ func TestMakeStorageDotConfigNoDriveLetters(t *testing.T) {
 		"SSD_Drive_Prefix": "/dev/ss",
 	}
 
-	params := makeParamsFromMap(*server.Profile, StorageFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], StorageFileName, paramData)
 
 	cfg, err := MakeStorageDotConfig(server, params, &StorageDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -173,7 +173,7 @@ func TestMakeStorageDotConfigSomeDriveLetters(t *testing.T) {
 	hdr := "myHeaderComment"
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	paramData := map[string]string{
 		"Drive_Prefix":     "/dev/sd",
@@ -182,7 +182,7 @@ func TestMakeStorageDotConfigSomeDriveLetters(t *testing.T) {
 		"Drive_Letters":    "a,b,c,d,e",
 	}
 
-	params := makeParamsFromMap(*server.Profile, StorageFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], StorageFileName, paramData)
 
 	cfg, err := MakeStorageDotConfig(server, params, &StorageDotConfigOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/sysctldotconf.go b/lib/go-atscfg/sysctldotconf.go
index bb2e765f45..f94879d978 100644
--- a/lib/go-atscfg/sysctldotconf.go
+++ b/lib/go-atscfg/sysctldotconf.go
@@ -45,8 +45,8 @@ func MakeSysCtlDotConf(
 		opt = &SysCtlDotConfOpts{}
 	}
 	warnings := []string{}
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server missing Profile")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing Profiles")
 	}
 
 	paramData, paramWarns := paramsToMap(filterParams(serverParams, SysctlFileName, "", "", "location"))
diff --git a/lib/go-atscfg/sysctldotconf_test.go b/lib/go-atscfg/sysctldotconf_test.go
index a93b8287c5..22947f1a27 100644
--- a/lib/go-atscfg/sysctldotconf_test.go
+++ b/lib/go-atscfg/sysctldotconf_test.go
@@ -34,9 +34,9 @@ func TestMakeSysCtlDotConf(t *testing.T) {
 	}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
-	params := makeParamsFromMap(*server.Profile, SysctlFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], SysctlFileName, paramData)
 
 	cfg, err := MakeSysCtlDotConf(server, params, &SysCtlDotConfOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/urlsigconfig.go b/lib/go-atscfg/urlsigconfig.go
index 35a2b91d21..01e7bf4c36 100644
--- a/lib/go-atscfg/urlsigconfig.go
+++ b/lib/go-atscfg/urlsigconfig.go
@@ -49,8 +49,8 @@ func MakeURLSigConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server missing Profile")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing Profiles")
 	}
 
 	paramData, paramWarns := paramsToMap(filterParams(serverParams, fileName, "", "", "location"))
diff --git a/lib/go-atscfg/urlsigconfig_test.go b/lib/go-atscfg/urlsigconfig_test.go
index a8a95e63bb..2262425211 100644
--- a/lib/go-atscfg/urlsigconfig_test.go
+++ b/lib/go-atscfg/urlsigconfig_test.go
@@ -40,11 +40,11 @@ func TestMakeURLSigConfig(t *testing.T) {
 	}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
 	fileName := "url_sig_myds.config"
 
-	params := makeParamsFromMap(*server.Profile, fileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], fileName, paramData)
 
 	cfg, err := MakeURLSigConfig(fileName, server, params, allURLSigKeys, &URLSigConfigOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-atscfg/volumedotconfig.go b/lib/go-atscfg/volumedotconfig.go
index ef58139f4c..261b07b9d8 100644
--- a/lib/go-atscfg/volumedotconfig.go
+++ b/lib/go-atscfg/volumedotconfig.go
@@ -49,8 +49,8 @@ func MakeVolumeDotConfig(
 	}
 	warnings := []string{}
 
-	if server.Profile == nil {
-		return Cfg{}, makeErr(warnings, "server missing Profile")
+	if len(server.ProfileNames) == 0 {
+		return Cfg{}, makeErr(warnings, "server missing Profiles")
 	}
 
 	paramData, paramWarns := paramsToMap(filterParams(serverParams, VolumeFileName, "", "", ""))
diff --git a/lib/go-atscfg/volumedotconfig_test.go b/lib/go-atscfg/volumedotconfig_test.go
index 8f3191161b..02ad8b0aa0 100644
--- a/lib/go-atscfg/volumedotconfig_test.go
+++ b/lib/go-atscfg/volumedotconfig_test.go
@@ -37,9 +37,9 @@ func TestMakeVolumeDotConfig(t *testing.T) {
 	}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
-	params := makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err := MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -59,7 +59,7 @@ func TestMakeVolumeDotConfig(t *testing.T) {
 
 	delete(paramData, "SSD_Drive_Prefix")
 	delete(paramData, "SSD_Drive_Letters")
-	params = makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params = makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err = MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -77,7 +77,7 @@ func TestMakeVolumeDotConfig(t *testing.T) {
 
 	delete(paramData, "RAM_Drive_Prefix")
 	delete(paramData, "RAM_Drive_Letters")
-	params = makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params = makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err = MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -100,9 +100,9 @@ func TestMakeVolumeDotConfigNoParams(t *testing.T) {
 	paramData := map[string]string{}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
-	params := makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err := MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -136,9 +136,9 @@ func TestMakeVolumeDotConfigNoLetters(t *testing.T) {
 	}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
-	params := makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err := MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
@@ -170,9 +170,9 @@ func TestMakeVolumeDotConfigSomePrefixes(t *testing.T) {
 	}
 
 	server := makeGenericServer()
-	server.Profile = &profileName
+	server.ProfileNames = []string{profileName}
 
-	params := makeParamsFromMap(*server.Profile, VolumeFileName, paramData)
+	params := makeParamsFromMap(server.ProfileNames[0], VolumeFileName, paramData)
 
 	cfg, err := MakeVolumeDotConfig(server, params, &VolumeDotConfigOpts{HdrComment: hdr})
 	if err != nil {
diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go
index cb5c3b702e..23971316b5 100644
--- a/lib/go-tc/servers.go
+++ b/lib/go-tc/servers.go
@@ -917,11 +917,9 @@ func (s ServerNullableV2) Upgrade() (ServerV30, error) {
 //
 // Deprecated: Traffic Ops API version 3 is deprecated, new code should use
 // ServerV40 or newer structures.
-func (s ServerV30) UpgradeToV40() (ServerV40, error) {
-	upgraded := ServerV40{
-		CommonServerProperties: s.CommonServerProperties,
-		StatusLastUpdated:      s.StatusLastUpdated,
-	}
+func (s ServerV30) UpgradeToV40(profileNames []string) (ServerV40, error) {
+	upgraded := UpdateServerPropertiesV40(profileNames, s.CommonServerProperties)
+	upgraded.StatusLastUpdated = s.StatusLastUpdated
 	infs, err := ToInterfacesV4(s.Interfaces, s.RouterHostName, s.RouterPortName)
 	if err != nil {
 		return upgraded, err
@@ -936,7 +934,7 @@ func (s ServerV30) UpgradeToV40() (ServerV40, error) {
 //
 // Deprecated: Traffic Ops API version 2 is deprecated, new code should use
 // ServerV40 or newer structures.
-func (s ServerNullableV2) UpgradeToV40() (ServerV40, error) {
+func (s ServerNullableV2) UpgradeToV40(profileNames []string) (ServerV40, error) {
 	ipv4IsService := false
 	if s.IPIsService != nil {
 		ipv4IsService = *s.IPIsService
@@ -945,9 +943,7 @@ func (s ServerNullableV2) UpgradeToV40() (ServerV40, error) {
 	if s.IP6IsService != nil {
 		ipv6IsService = *s.IP6IsService
 	}
-	upgraded := ServerV40{
-		CommonServerProperties: s.CommonServerProperties,
-	}
+	upgraded := UpdateServerPropertiesV40(profileNames, s.CommonServerProperties)
 
 	infs, err := s.LegacyInterfaceDetails.ToInterfacesV4(ipv4IsService, ipv6IsService, s.RouterHostName, s.RouterPortName)
 	if err != nil {
@@ -957,9 +953,84 @@ func (s ServerNullableV2) UpgradeToV40() (ServerV40, error) {
 	return upgraded, nil
 }
 
+// UpdateServerPropertiesV40 updates CommonServerProperties of V2 and V3 to ServerV40
+func UpdateServerPropertiesV40(profileNames []string, properties CommonServerProperties) ServerV40 {
+	return ServerV40{
+		Cachegroup:       properties.Cachegroup,
+		CachegroupID:     properties.CachegroupID,
+		CDNID:            properties.CDNID,
+		CDNName:          properties.CDNName,
+		DeliveryServices: properties.DeliveryServices,
+		DomainName:       properties.DomainName,
+		FQDN:             properties.FQDN,
+		FqdnTime:         properties.FqdnTime,
+		GUID:             properties.GUID,
+		HostName:         properties.HostName,
+		HTTPSPort:        properties.HTTPSPort,
+		ID:               properties.ID,
+		ILOIPAddress:     properties.ILOIPAddress,
+		ILOIPGateway:     properties.ILOIPGateway,
+		ILOIPNetmask:     properties.ILOIPNetmask,
+		ILOPassword:      properties.ILOPassword,
+		ILOUsername:      properties.ILOUsername,
+		LastUpdated:      properties.LastUpdated,
+		MgmtIPAddress:    properties.MgmtIPAddress,
+		MgmtIPGateway:    properties.MgmtIPGateway,
+		MgmtIPNetmask:    properties.MgmtIPNetmask,
+		OfflineReason:    properties.OfflineReason,
+		ProfileNames:     profileNames,
+		PhysLocation:     properties.PhysLocation,
+		PhysLocationID:   properties.PhysLocationID,
+		Rack:             properties.Rack,
+		RevalPending:     properties.RevalPending,
+		Status:           properties.Status,
+		StatusID:         properties.StatusID,
+		TCPPort:          properties.TCPPort,
+		Type:             properties.Type,
+		TypeID:           properties.TypeID,
+		UpdPending:       properties.UpdPending,
+		XMPPID:           properties.XMPPID,
+		XMPPPasswd:       properties.XMPPPasswd,
+	}
+}
+
 // ServerV40 is the representation of a Server in version 4.0 of the Traffic Ops API.
 type ServerV40 struct {
-	CommonServerProperties
+	Cachegroup        *string                  `json:"cachegroup" db:"cachegroup"`
+	CachegroupID      *int                     `json:"cachegroupId" db:"cachegroup_id"`
+	CDNID             *int                     `json:"cdnId" db:"cdn_id"`
+	CDNName           *string                  `json:"cdnName" db:"cdn_name"`
+	DeliveryServices  *map[string][]string     `json:"deliveryServices,omitempty"`
+	DomainName        *string                  `json:"domainName" db:"domain_name"`
+	FQDN              *string                  `json:"fqdn,omitempty"`
+	FqdnTime          time.Time                `json:"-"`
+	GUID              *string                  `json:"guid" db:"guid"`
+	HostName          *string                  `json:"hostName" db:"host_name"`
+	HTTPSPort         *int                     `json:"httpsPort" db:"https_port"`
+	ID                *int                     `json:"id" db:"id"`
+	ILOIPAddress      *string                  `json:"iloIpAddress" db:"ilo_ip_address"`
+	ILOIPGateway      *string                  `json:"iloIpGateway" db:"ilo_ip_gateway"`
+	ILOIPNetmask      *string                  `json:"iloIpNetmask" db:"ilo_ip_netmask"`
+	ILOPassword       *string                  `json:"iloPassword" db:"ilo_password"`
+	ILOUsername       *string                  `json:"iloUsername" db:"ilo_username"`
+	LastUpdated       *TimeNoMod               `json:"lastUpdated" db:"last_updated"`
+	MgmtIPAddress     *string                  `json:"mgmtIpAddress" db:"mgmt_ip_address"`
+	MgmtIPGateway     *string                  `json:"mgmtIpGateway" db:"mgmt_ip_gateway"`
+	MgmtIPNetmask     *string                  `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"`
+	OfflineReason     *string                  `json:"offlineReason" db:"offline_reason"`
+	PhysLocation      *string                  `json:"physLocation" db:"phys_location"`
+	PhysLocationID    *int                     `json:"physLocationId" db:"phys_location_id"`
+	ProfileNames      []string                 `json:"profileNames" db:"profile_name"`
+	Rack              *string                  `json:"rack" db:"rack"`
+	RevalPending      *bool                    `json:"revalPending" db:"reval_pending"`
+	Status            *string                  `json:"status" db:"status"`
+	StatusID          *int                     `json:"statusId" db:"status_id"`
+	TCPPort           *int                     `json:"tcpPort" db:"tcp_port"`
+	Type              string                   `json:"type" db:"server_type"`
+	TypeID            *int                     `json:"typeId" db:"server_type_id"`
+	UpdPending        *bool                    `json:"updPending" db:"upd_pending"`
+	XMPPID            *string                  `json:"xmppId" db:"xmpp_id"`
+	XMPPPasswd        *string                  `json:"xmppPasswd" db:"xmpp_passwd"`
 	Interfaces        []ServerInterfaceInfoV40 `json:"interfaces" db:"interfaces"`
 	StatusLastUpdated *time.Time               `json:"statusLastUpdated" db:"status_last_updated"`
 	ConfigUpdateTime  *time.Time               `json:"configUpdateTime" db:"config_update_time"`
@@ -1026,7 +1097,7 @@ func (s *ServerV30) ToServerV2() (ServerNullableV2, error) {
 //
 // Deprecated: Traffic Ops API version 3 is deprecated, new code should use
 // ServerV40 or newer structures.
-func (s *ServerV40) ToServerV3FromV4() (ServerV30, error) {
+func (s *ServerV40) ToServerV3FromV4(csp CommonServerProperties) (ServerV30, error) {
 	routerHostName := ""
 	routerPortName := ""
 	interfaces := make([]ServerInterfaceInfo, 0)
@@ -1046,7 +1117,7 @@ func (s *ServerV40) ToServerV3FromV4() (ServerV30, error) {
 		interfaces = append(interfaces, i)
 	}
 	serverV30 := ServerV30{
-		CommonServerProperties: s.CommonServerProperties,
+		CommonServerProperties: csp,
 		Interfaces:             interfaces,
 		StatusLastUpdated:      s.StatusLastUpdated,
 	}
@@ -1063,12 +1134,12 @@ func (s *ServerV40) ToServerV3FromV4() (ServerV30, error) {
 //
 // Deprecated: Traffic Ops API version 2 is deprecated, new code should use
 // ServerV40 or newer structures.
-func (s *ServerV40) ToServerV2FromV4() (ServerNullableV2, error) {
+func (s *ServerV40) ToServerV2FromV4(csp CommonServerProperties) (ServerNullableV2, error) {
 	routerHostName := ""
 	routerPortName := ""
 	legacyServer := ServerNullableV2{
 		ServerNullableV11: ServerNullableV11{
-			CommonServerProperties: s.CommonServerProperties,
+			CommonServerProperties: csp,
 		},
 		IPIsService:  new(bool),
 		IP6IsService: new(bool),
diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go
index cb17e52715..9641813ba3 100644
--- a/lib/go-tc/servers_test.go
+++ b/lib/go-tc/servers_test.go
@@ -19,10 +19,12 @@ package tc
  * under the License.
  */
 
-import "fmt"
-import "strings"
-import "testing"
-import "time"
+import (
+	"fmt"
+	"strings"
+	"testing"
+	"time"
+)
 
 func ExampleLegacyInterfaceDetails_ToInterfaces() {
 	lid := LegacyInterfaceDetails{
diff --git a/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.down.sql b/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.down.sql
new file mode 100644
index 0000000000..261b7f2ce1
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.down.sql
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+DROP TABLE IF EXISTS public.server_profile;
+
+CREATE TRIGGER before_update_ip_address_trigger
+    BEFORE UPDATE ON ip_address
+    FOR EACH ROW WHEN (NEW.address <> OLD.address)
+    EXECUTE PROCEDURE before_ip_address_table();
+
+CREATE TRIGGER before_create_ip_address_trigger
+    BEFORE INSERT ON ip_address
+    FOR EACH ROW EXECUTE PROCEDURE before_ip_address_table();
+
+CREATE TRIGGER before_update_server_trigger
+    BEFORE UPDATE ON server
+    FOR EACH ROW WHEN (NEW.profile <> OLD.profile)
+    EXECUTE PROCEDURE before_server_table();
+
+CREATE TRIGGER before_create_server_trigger
+    BEFORE INSERT ON server
+    FOR EACH ROW EXECUTE PROCEDURE before_server_table();
diff --git a/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.up.sql b/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.up.sql
new file mode 100644
index 0000000000..428e678e4b
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2022041410185700_add_server_profile_table.up.sql
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership.  The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations undera
+ * the License.
+ */
+
+CREATE TABLE IF NOT EXISTS public.server_profile (
+                                                     server bigint NOT NULL,
+                                                     profile_name text NOT NULL,
+                                                     priority int NOT NULL CHECK (priority >= 0),
+    CONSTRAINT pk_server_profile PRIMARY KEY (profile_name, server),
+    CONSTRAINT fk_server_id FOREIGN KEY (server) REFERENCES public.server(id) ON DELETE CASCADE ON UPDATE CASCADE,
+    CONSTRAINT fk_server_profile_name_profile FOREIGN KEY (profile_name) REFERENCES public.profile(name) ON UPDATE CASCADE ON DELETE RESTRICT
+    );
+
+INSERT into public.server_profile(server, profile_name, priority)
+SELECT s.id, p.name, 0
+FROM public.server AS s
+    JOIN public.profile p ON p.id=s.profile;
+
+DROP TRIGGER IF EXISTS before_update_ip_address_trigger on ip_address;
+
+DROP TRIGGER IF EXISTS before_create_ip_address_trigger on ip_address;
+
+DROP TRIGGER IF EXISTS before_update_server_trigger ON server;
+
+DROP TRIGGER IF EXISTS before_create_server_trigger ON server;
diff --git a/traffic_ops/testing/api/v4/cdn_queue_updates_by_type_profile_test.go b/traffic_ops/testing/api/v4/cdn_queue_updates_by_type_profile_test.go
index 4757360597..74179ce5cf 100644
--- a/traffic_ops/testing/api/v4/cdn_queue_updates_by_type_profile_test.go
+++ b/traffic_ops/testing/api/v4/cdn_queue_updates_by_type_profile_test.go
@@ -140,7 +140,7 @@ func QueueUpdatesByProfile(t *testing.T) {
 	}
 	server := testData.Servers[0]
 	opts := client.NewRequestOptions()
-	if server.CDNName == nil || server.Profile == nil {
+	if server.CDNName == nil || len(server.ProfileNames) == 0 {
 		t.Fatalf("server doesn't have a CDN name or a profile name...quitting")
 	}
 
@@ -157,7 +157,7 @@ func QueueUpdatesByProfile(t *testing.T) {
 	opts.QueryParameters.Del("name")
 
 	// Get the first server's Profile ID
-	opts.QueryParameters.Set("name", *server.Profile)
+	opts.QueryParameters.Set("name", server.ProfileNames[0])
 	profiles, _, err := TOSession.GetProfiles(opts)
 	if err != nil {
 		t.Fatalf("error while getting profiles: %v", err)
diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go
index 5970444220..4adf0d8345 100644
--- a/traffic_ops/testing/api/v4/servers_test.go
+++ b/traffic_ops/testing/api/v4/servers_test.go
@@ -218,8 +218,8 @@ func LastServerInTopologyCacheGroup(t *testing.T) {
 		t.Fatalf("expected to get %d server from cdn %s from cachegroup %s in topology %s, got %d servers", expectedLength, cdnName, cacheGroupName, topologyName, len(servers.Response))
 	}
 	server := servers.Response[0]
-	if server.ID == nil || server.CDNID == nil || server.ProfileID == nil || server.CachegroupID == nil || server.HostName == nil {
-		t.Fatal("Traffic Ops returned a representation for a server with null or undefined ID and/or CDN ID and/or Profile ID and/or Cache Group ID and/or Host Name")
+	if server.ID == nil || server.CDNID == nil || len(server.ProfileNames) == 0 || server.CachegroupID == nil || server.HostName == nil {
+		t.Fatal("Traffic Ops returned a representation for a server with null or undefined ID and/or CDN ID and/or Profile Names and/or Cache Group ID and/or Host Name")
 	}
 
 	_, reqInf, err := TOSession.DeleteServer(*server.ID, client.RequestOptions{})
@@ -250,15 +250,26 @@ func LastServerInTopologyCacheGroup(t *testing.T) {
 	if len(profiles.Response) != 1 {
 		t.Fatalf("Expected exactly one Profile to exist with name 'MID1', found: %d", len(profiles.Response))
 	}
-	newProfile := profiles.Response[0].ID
-	oldProfile := *server.ProfileID
-	server.ProfileID = &newProfile
+	newProfileID := profiles.Response[0].ID
+	oldProfileName := server.ProfileNames[0]
+
+	opts.QueryParameters.Set("id", strconv.Itoa(newProfileID))
+	nps, _, err := TOSession.GetProfiles(opts)
+	if err != nil {
+		t.Fatalf("failed to query profiles: %v", err)
+	}
+	if len(nps.Response) != 1 {
+		t.Fatalf("Expected exactly one Profile to exist, found: %d", len(profiles.Response))
+	}
+	server.ProfileNames = []string{nps.Response[0].Name}
+	opts.QueryParameters.Del("id")
+
 	_, _, err = TOSession.UpdateServer(*server.ID, server, client.RequestOptions{})
 	if err == nil {
 		t.Fatalf("changing the CDN of the last server (%s) in a CDN in a cachegroup used by a topology assigned to a delivery service(s) in that CDN - expected: error, actual: nil", *server.HostName)
 	}
 	server.CDNID = &oldCDNID
-	server.ProfileID = &oldProfile
+	server.ProfileNames = []string{oldProfileName}
 
 	opts.QueryParameters.Set("name", moveToCacheGroup)
 	cgs, _, err := TOSession.GetCacheGroups(opts)
@@ -606,24 +617,23 @@ func CreateTestServerWithoutProfileID(t *testing.T) {
 	}
 
 	server := resp.Response[0]
-	if server.Profile == nil || server.ID == nil || server.HostName == nil {
+	if len(server.ProfileNames) == 0 || server.ID == nil || server.HostName == nil {
 		t.Fatal("Traffic Ops returned a representation of a server with null or undefined ID and/or Profile and/or Host Name")
 	}
-	originalProfile := *server.Profile
+	originalProfile := server.ProfileNames
 	delResp, _, err := TOSession.DeleteServer(*server.ID, client.RequestOptions{})
 	if err != nil {
 		t.Fatalf("cannot delete Server by ID %d: %v - %v", *server.ID, err, delResp)
 	}
 
-	*server.Profile = ""
-	server.ProfileID = nil
+	server.ProfileNames = []string{""}
 	_, reqInfo, _ := TOSession.CreateServer(server, client.RequestOptions{})
 	if reqInfo.StatusCode != 400 {
 		t.Fatalf("Expected status code: %v but got: %v", "400", reqInfo.StatusCode)
 	}
 
 	//Reverting it back for further tests
-	*server.Profile = originalProfile
+	server.ProfileNames = originalProfile
 	response, _, err := TOSession.CreateServer(server, client.RequestOptions{})
 	if err != nil {
 		t.Fatalf("could not create server: %v - alerts: %+v", err, response.Alerts)
@@ -950,10 +960,16 @@ func GetTestServersQueryParameters(t *testing.T) {
 		opts.QueryParameters.Del("status")
 	}
 
-	if s.ProfileID == nil {
+	opts.QueryParameters.Add("name", s.ProfileNames[0])
+	pr, _, err := TOSession.GetProfiles(opts)
+	if err != nil {
+		t.Fatalf("failed to query profile: %v", err)
+	}
+	if len(pr.Response) != 1 {
 		t.Error("Found server with no Profile ID")
 	} else {
-		opts.QueryParameters.Add("profileId", strconv.Itoa(*s.ProfileID))
+		profileID := pr.Response[0].ID
+		opts.QueryParameters.Add("profileId", strconv.Itoa(profileID))
 		if resp, _, err := TOSession.GetServers(opts); err != nil {
 			t.Errorf("Error getting servers by Profile ID: %v - alerts: %+v", err, resp.Alerts)
 		}
@@ -995,26 +1011,24 @@ func UniqueIPProfileTestServers(t *testing.T) {
 		}
 	}
 	_, _, err = TOSession.CreateServer(tc.ServerV40{
-		CommonServerProperties: tc.CommonServerProperties{
-			Cachegroup: server.Cachegroup,
-			CDNName:    server.CDNName,
-			DomainName: util.StrPtr("mydomain"),
-			FQDN:       util.StrPtr("myfqdn"),
-			FqdnTime:   time.Time{},
-			HostName:   util.StrPtr("myhostname"),
-			HTTPSPort:  util.IntPtr(443),
-			LastUpdated: &tc.TimeNoMod{
-				Time:  time.Time{},
-				Valid: false,
-			},
-			PhysLocation: server.PhysLocation,
-			Profile:      server.Profile,
-			StatusID:     server.StatusID,
-			Type:         server.Type,
-			UpdPending:   util.BoolPtr(false),
-			XMPPID:       &xmppID,
+		Cachegroup: server.Cachegroup,
+		CDNName:    server.CDNName,
+		DomainName: util.StrPtr("mydomain"),
+		FQDN:       util.StrPtr("myfqdn"),
+		FqdnTime:   time.Time{},
+		HostName:   util.StrPtr("myhostname"),
+		HTTPSPort:  util.IntPtr(443),
+		LastUpdated: &tc.TimeNoMod{
+			Time:  time.Time{},
+			Valid: false,
 		},
-		Interfaces: server.Interfaces,
+		PhysLocation: server.PhysLocation,
+		ProfileNames: server.ProfileNames,
+		StatusID:     server.StatusID,
+		Type:         server.Type,
+		UpdPending:   util.BoolPtr(false),
+		XMPPID:       &xmppID,
+		Interfaces:   server.Interfaces,
 	}, client.RequestOptions{})
 
 	if err == nil {
diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json b/traffic_ops/testing/api/v4/tc-fixtures.json
index 744566dac4..7af7a485c8 100644
--- a/traffic_ops/testing/api/v4/tc-fixtures.json
+++ b/traffic_ops/testing/api/v4/tc-fixtures.json
@@ -2640,7 +2640,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2684,7 +2684,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGEInCDN2",
+            "profileNames": ["EDGEInCDN2"],
             "rack": "",
             "revalPending": false,
             "status": "REPORTED",
@@ -2733,7 +2733,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2782,7 +2782,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2831,7 +2831,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2880,7 +2880,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2930,7 +2930,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -2979,7 +2979,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3028,7 +3028,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MID2",
+            "profileNames": ["MID2"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3077,7 +3077,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3126,7 +3126,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3175,7 +3175,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3224,7 +3224,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "RASCAL1",
+            "profileNames": ["RASCAL1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3273,7 +3273,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3322,7 +3322,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "ONLINE",
@@ -3371,7 +3371,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MSO",
+            "profileNames": ["MSO"],
             "rack": "RR 119.02",
             "revalPending": true,
             "status": "REPORTED",
@@ -3420,7 +3420,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MSO",
+            "profileNames": ["MSO"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "ONLINE",
@@ -3469,7 +3469,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MSO-CDN2",
+            "profileNames": ["MSO-CDN2"],
             "rack": "RR 119.02",
             "revalPending": true,
             "status": "REPORTED",
@@ -3518,7 +3518,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3567,7 +3567,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3605,7 +3605,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3641,7 +3641,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3677,7 +3677,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3713,7 +3713,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3749,7 +3749,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3785,7 +3785,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3821,7 +3821,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3857,7 +3857,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3893,7 +3893,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3929,7 +3929,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -3965,7 +3965,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4001,7 +4001,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4037,7 +4037,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4073,7 +4073,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4109,7 +4109,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4145,7 +4145,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4181,7 +4181,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4217,7 +4217,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4253,7 +4253,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4300,7 +4300,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4338,7 +4338,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4374,7 +4374,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4410,7 +4410,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4446,7 +4446,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4482,7 +4482,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_EDGE",
+            "profileNames": ["CDN2_EDGE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4518,7 +4518,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4554,7 +4554,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4590,7 +4590,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4626,7 +4626,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4662,7 +4662,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4698,7 +4698,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": true,
             "status": "OFFLINE",
@@ -4734,7 +4734,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4770,7 +4770,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "CDN2_MID",
+            "profileNames": ["CDN2_MID"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4799,7 +4799,7 @@
                 }
             ],
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "revalPending": false,
             "status": "ONLINE",
             "tcpPort": 80,
@@ -4845,7 +4845,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1", "ATS_EDGE_TIER_CACHE"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4894,7 +4894,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "MID1",
+            "profileNames": ["MID1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -4943,7 +4943,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": true,
             "status": "REPORTED",
@@ -4992,7 +4992,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -5043,7 +5043,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -5094,7 +5094,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "REPORTED",
@@ -5142,7 +5142,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
 			"revalUpdateTime": "2022-01-01T17:00:00-07:00",
@@ -5193,7 +5193,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
 			"revalUpdateTime": "2022-01-01T17:00:00-07:00",
 			"revalApplyTime": "1969-12-31T17:00:00-07:00",
@@ -5245,7 +5245,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
 			"revalUpdateTime": "2022-01-01T17:00:00-07:00",
@@ -5298,7 +5298,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
 			"revalUpdateTime": "2022-01-01T17:00:00-07:00",
 			"revalApplyTime": "1969-12-31T17:00:00-07:00",
@@ -5347,7 +5347,7 @@
             "mgmtIpNetmask": "",
             "offlineReason": null,
             "physLocation": "Denver",
-            "profile": "EDGE1",
+            "profileNames": ["EDGE1"],
             "rack": "RR 119.02",
             "revalPending": false,
             "status": "ADMIN_DOWN",
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index 72599ca03e..024ed7fd83 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -1979,3 +1979,114 @@ WHERE server.id = $2;`
 
 	return nil
 }
+
+// GetCommonServerPropertiesFromV4 converts ServerV40 to CommonServerProperties struct.
+func GetCommonServerPropertiesFromV4(s tc.ServerV40, tx *sql.Tx) (tc.CommonServerProperties, error) {
+	var id int
+	var desc string
+	if len(s.ProfileNames) == 0 {
+		return tc.CommonServerProperties{}, fmt.Errorf("profileName doesnot exist in server: %v", *s.ID)
+	}
+	rows, err := tx.Query("SELECT id, description from profile WHERE name=$1", (s.ProfileNames)[0])
+	if err != nil {
+		return tc.CommonServerProperties{}, fmt.Errorf("querying profile id and description by profile_name: %w", err)
+	}
+	defer log.Close(rows, "closing rows in GetCommonServerPropertiesFromV4")
+
+	for rows.Next() {
+		if err := rows.Scan(&id, &desc); err != nil {
+			return tc.CommonServerProperties{}, fmt.Errorf("scanning profile: %w", err)
+		}
+	}
+
+	return tc.CommonServerProperties{
+		Cachegroup:       s.Cachegroup,
+		CachegroupID:     s.CachegroupID,
+		CDNID:            s.CDNID,
+		CDNName:          s.CDNName,
+		DeliveryServices: s.DeliveryServices,
+		DomainName:       s.DomainName,
+		FQDN:             s.FQDN,
+		FqdnTime:         s.FqdnTime,
+		GUID:             s.GUID,
+		HostName:         s.HostName,
+		HTTPSPort:        s.HTTPSPort,
+		ID:               s.ID,
+		ILOIPAddress:     s.ILOIPAddress,
+		ILOIPGateway:     s.ILOIPGateway,
+		ILOIPNetmask:     s.ILOIPNetmask,
+		ILOPassword:      s.ILOPassword,
+		ILOUsername:      s.ILOUsername,
+		LastUpdated:      s.LastUpdated,
+		MgmtIPAddress:    s.MgmtIPAddress,
+		MgmtIPGateway:    s.MgmtIPGateway,
+		MgmtIPNetmask:    s.MgmtIPNetmask,
+		OfflineReason:    s.OfflineReason,
+		Profile:          &(s.ProfileNames)[0],
+		ProfileDesc:      &desc,
+		ProfileID:        &id,
+		PhysLocation:     s.PhysLocation,
+		PhysLocationID:   s.PhysLocationID,
+		Rack:             s.Rack,
+		RevalPending:     s.RevalPending,
+		Status:           s.Status,
+		StatusID:         s.StatusID,
+		TCPPort:          s.TCPPort,
+		Type:             s.Type,
+		TypeID:           s.TypeID,
+		UpdPending:       s.UpdPending,
+		XMPPID:           s.XMPPID,
+		XMPPPasswd:       s.XMPPPasswd,
+	}, nil
+}
+
+// UpdateServerProfilesForV4 updates server_profile table via update function for APIv4.
+func UpdateServerProfilesForV4(id int, profile []string, tx *sql.Tx) error {
+	profileNames := make([]string, 0, len(profile))
+	priority := make([]int, 0, len(profile))
+	for i, _ := range profile {
+		priority = append(priority, i)
+	}
+
+	//Delete existing rows from server_profile to get the priority correct for profile_name changes
+	_, err := tx.Exec("DELETE FROM server_profile WHERE server=$1", id)
+	if err != nil {
+		return fmt.Errorf("updating server_profile by server id: %d, error: %w", id, err)
+	}
+
+	query := `WITH inserted AS (
+		INSERT INTO server_profile
+		SELECT $1, "profile_name", "priority"
+		FROM UNNEST($2::text[], $3::int[]) AS tmp("profile_name", "priority")
+		RETURNING profile_name, priority
+	)
+	SELECT ARRAY_AGG(profile_name)
+	FROM (
+		SELECT profile_name
+		FROM inserted
+		ORDER BY priority ASC
+	) AS returned(profile_name)
+`
+	err = tx.QueryRow(query, id, pq.Array(profile), pq.Array(priority)).Scan(pq.Array(&profileNames))
+	if err != nil {
+		return fmt.Errorf("failed to insert/read into/from server_profile table, %w", err)
+	}
+	return nil
+}
+
+// UpdateServerProfileTableForV2V3 updates CommonServerPropertiesV40 struct and server_profile table via Update (server) function for API v2/v3.
+func UpdateServerProfileTableForV2V3(id *int, newProfile *string, origProfile string, tx *sql.Tx) ([]string, error) {
+	var profileName []string
+	query := `UPDATE server_profile SET profile_name=$1 WHERE server=$2 AND profile_name=$3`
+	_, err := tx.Exec(query, *newProfile, *id, origProfile)
+	if err != nil {
+		return nil, fmt.Errorf("updating server_profile by profile_name: %w", err)
+	}
+
+	err = tx.QueryRow("SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE server=$1", *id).Scan(pq.Array(&profileName))
+	if err == sql.ErrNoRows {
+		return nil, fmt.Errorf("selecting server_profile by profile_name: %w", err)
+	}
+
+	return profileName, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go
index ad624a2a7d..908195bdda 100644
--- a/traffic_ops/traffic_ops_golang/server/servers.go
+++ b/traffic_ops/traffic_ops_golang/server/servers.go
@@ -135,9 +135,7 @@ SELECT
 	s.offline_reason,
 	pl.name AS phys_location,
 	s.phys_location AS phys_location_id,
-	p.name AS profile,
-	p.description AS profile_desc,
-	s.profile AS profile_id,
+	(SELECT ARRAY_AGG(sp.profile_name) FROM server_profile AS sp where sp.server=s.id) AS profile_name,
 	s.rack,
 	s.revalidate_update_time > s.revalidate_apply_time AS reval_pending,
 	s.revalidate_update_time,
@@ -223,6 +221,7 @@ INSERT INTO server (
 ) RETURNING
 	id
 `
+
 const insertQueryV4 = `
 INSERT INTO server (
 	cachegroup,
@@ -248,30 +247,36 @@ INSERT INTO server (
 	xmpp_id,
 	xmpp_passwd
 ) VALUES (
-	:cachegroup_id,
-	:cdn_id,
-	:domain_name,
-	:host_name,
-	:https_port,
-	:ilo_ip_address,
-	:ilo_ip_netmask,
-	:ilo_ip_gateway,
-	:ilo_username,
-	:ilo_password,
-	:mgmt_ip_address,
-	:mgmt_ip_netmask,
-	:mgmt_ip_gateway,
-	:offline_reason,
-	:phys_location_id,
-	:profile_id,
-	:rack,
-	:status_id,
-	:tcp_port,
-	:server_type_id,
-	:xmpp_id,
-	:xmpp_passwd
+    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22
 ) RETURNING
-	id
+	(SELECT name FROM cachegroup WHERE cachegroup.id=server.cachegroup) AS cachegroup,
+	cachegroup AS cachegroup_id,
+	cdn_id,
+	(SELECT name FROM cdn WHERE cdn.id=server.cdn_id) AS cdn_name,
+	domain_name,
+	guid,
+	host_name,
+	https_port,
+	id,
+	ilo_ip_address,
+	ilo_ip_gateway,
+	ilo_ip_netmask,
+	ilo_password,
+	ilo_username,
+	last_updated,
+	mgmt_ip_address,
+	mgmt_ip_gateway,
+	mgmt_ip_netmask,
+	offline_reason,
+	(SELECT name FROM phys_location WHERE phys_location.id=server.phys_location) AS phys_location,
+	phys_location AS phys_location_id,
+	(SELECT ARRAY[name] FROM profile WHERE profile.id=server.profile) AS profile_name,
+	rack,
+	(SELECT name FROM status WHERE status.id=server.status) AS status,
+	status AS status_id,
+	tcp_port,
+	(SELECT name FROM type WHERE type.id=server.type) AS server_type,
+	type AS server_type_id
 `
 
 const insertQuery = `
@@ -342,7 +347,7 @@ UPDATE server SET
 	mgmt_ip_gateway=:mgmt_ip_gateway,
 	offline_reason=:offline_reason,
 	phys_location=:phys_location_id,
-	profile=:profile_id,
+	profile=(SELECT id from profile where name=(SELECT profile_name from server_profile sp WHERE sp.server=:id and priority=0)),
 	rack=:rack,
 	status=:status_id,
 	tcp_port=:tcp_port,
@@ -351,7 +356,35 @@ UPDATE server SET
 	status_last_updated=:status_last_updated
 WHERE id=:id
 RETURNING
-	id
+	(SELECT name FROM cachegroup WHERE cachegroup.id=server.cachegroup) AS cachegroup,
+	cachegroup AS cachegroup_id,
+	cdn_id,
+	(SELECT name FROM cdn WHERE cdn.id=server.cdn_id) AS cdn_name,
+	domain_name,
+	guid,
+	host_name,
+	https_port,
+	id,
+	ilo_ip_address,
+	ilo_ip_gateway,
+	ilo_ip_netmask,
+	ilo_password,
+	ilo_username,
+	last_updated,
+	mgmt_ip_address,
+	mgmt_ip_gateway,
+	mgmt_ip_netmask,
+	offline_reason,
+	(SELECT name FROM phys_location WHERE phys_location.id=server.phys_location) AS phys_location,
+	phys_location AS phys_location_id,
+	(SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE server_profile.server=server.id) AS profile_name,
+	rack,
+	(SELECT name FROM status WHERE status.id=server.status) AS status,
+	status AS status_id,
+	tcp_port,
+	(SELECT name FROM type WHERE type.id=server.type) AS server_type,
+	type AS server_type_id,
+	status_last_updated
 `
 
 const originServerQuery = `
@@ -409,10 +442,56 @@ func validateCommon(s *tc.CommonServerProperties, tx *sql.Tx) []error {
 	if cdnID != *s.CDNID {
 		errs = append(errs, fmt.Errorf("CDN id '%d' for profile '%d' does not match Server CDN '%d'", cdnID, *s.ProfileID, *s.CDNID))
 	}
-
 	return errs
 }
 
+func validateCommonV40(s *tc.ServerV40, tx *sql.Tx) ([]error, error) {
+
+	noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot contain spaces")
+
+	errs := tovalidate.ToErrors(validation.Errors{
+		"cachegroupId":   validation.Validate(s.CachegroupID, validation.NotNil),
+		"cdnId":          validation.Validate(s.CDNID, validation.NotNil),
+		"domainName":     validation.Validate(s.DomainName, validation.Required, noSpaces),
+		"hostName":       validation.Validate(s.HostName, validation.Required, noSpaces),
+		"physLocationId": validation.Validate(s.PhysLocationID, validation.NotNil),
+		"profileNames":   validation.Validate(s.ProfileNames, validation.NotNil),
+		"statusId":       validation.Validate(s.StatusID, validation.NotNil),
+		"typeId":         validation.Validate(s.TypeID, validation.NotNil),
+		"httpsPort":      validation.Validate(s.HTTPSPort, validation.By(tovalidate.IsValidPortNumber)),
+		"tcpPort":        validation.Validate(s.TCPPort, validation.By(tovalidate.IsValidPortNumber)),
+	})
+
+	if len(errs) > 0 {
+		return errs, nil
+	}
+
+	if _, err := tc.ValidateTypeID(tx, s.TypeID, "server"); err != nil {
+		errs = append(errs, err)
+	}
+
+	if len(s.ProfileNames) == 0 {
+		errs = append(errs, fmt.Errorf("no profiles exists"))
+	}
+
+	var cdnID int
+	if err := tx.QueryRow("SELECT cdn from profile WHERE name=$1", s.ProfileNames[0]).Scan(&cdnID); err != nil {
+		log.Errorf("could not execute select cdnID from profile: %s\n", err)
+		if errors.Is(err, sql.ErrNoRows) {
+			errs = append(errs, fmt.Errorf("no such profileName: '%s'", s.ProfileNames[0]))
+		} else {
+			return nil, fmt.Errorf("unable to get CDN ID for profile name '%s': %w", s.ProfileNames[0], err)
+		}
+		return errs, nil
+	}
+
+	log.Infof("got cdn id: %d from profile and cdn id: %d from server", cdnID, *s.CDNID)
+	if cdnID != *s.CDNID {
+		errs = append(errs, fmt.Errorf("CDN id '%d' for profile '%v' does not match Server CDN '%d'", cdnID, s.ProfileNames[0], *s.CDNID))
+	}
+	return errs, nil
+}
+
 func validateV1(s *tc.ServerNullableV11, tx *sql.Tx) error {
 	if s.IP6Address != nil && len(strings.TrimSpace(*s.IP6Address)) == 0 {
 		s.IP6Address = nil
@@ -487,9 +566,9 @@ func validateMTU(mtu interface{}) error {
 	return nil
 }
 
-func validateV4(s *tc.ServerV40, tx *sql.Tx) (string, error) {
+func validateV4(s *tc.ServerV40, tx *sql.Tx) (string, error, error) {
 	if len(s.Interfaces) == 0 {
-		return "", errors.New("a server must have at least one interface")
+		return "", errors.New("a server must have at least one interface"), nil
 	}
 	var errs []error
 	var serviceAddrV4Found bool
@@ -498,7 +577,6 @@ func validateV4(s *tc.ServerV40, tx *sql.Tx) (string, error) {
 	var ipv6 string
 	var serviceInterface string
 	for _, iface := range s.Interfaces {
-
 		ruleName := fmt.Sprintf("interface '%s' ", iface.Name)
 		errs = append(errs, tovalidate.ToErrors(validation.Errors{
 			ruleName + "name":        validation.Validate(iface.Name, validation.Required),
@@ -551,25 +629,28 @@ func validateV4(s *tc.ServerV40, tx *sql.Tx) (string, error) {
 	if !serviceAddrV6Found && !serviceAddrV4Found {
 		errs = append(errs, errors.New("a server must have at least one service address"))
 	}
-
-	if errs = append(errs, validateCommon(&s.CommonServerProperties, tx)...); errs != nil {
-		return serviceInterface, util.JoinErrs(errs)
+	usrErr, sysErr := validateCommonV40(s, tx)
+	errs = append(errs, usrErr...)
+	if sysErr != nil || len(errs) > 0 {
+		return serviceInterface, util.JoinErrs(errs), sysErr
 	}
 	query := `
-SELECT s.ID, ip.address FROM server s
-JOIN profile p on p.Id = s.Profile
-JOIN interface i on i.server = s.ID
-JOIN ip_address ip on ip.Server = s.ID and ip.interface = i.name
-WHERE ip.service_address = true
-and p.id = $1
+SELECT tmp.server, ip.address
+FROM (
+  SELECT server, ARRAY_AGG(profile_name order by priority) AS profiles
+	FROM server_profile
+	GROUP BY server
+) AS tmp
+JOIN ip_address ip on ip.server = tmp.server
+WHERE (profiles = $1::text[])
 `
 	var rows *sql.Rows
 	var err error
 	//ProfileID already validated
 	if s.ID != nil {
-		rows, err = tx.Query(query+" and s.id != $2", *s.ProfileID, *s.ID)
+		rows, err = tx.Query(query+" and tmp.server != $2", pq.Array(s.ProfileNames), *s.ID)
 	} else {
-		rows, err = tx.Query(query, *s.ProfileID)
+		rows, err = tx.Query(query, pq.Array(s.ProfileNames))
 	}
 	if err != nil {
 		errs = append(errs, errors.New("unable to determine service address uniqueness"))
@@ -587,7 +668,7 @@ and p.id = $1
 		}
 	}
 
-	return serviceInterface, util.JoinErrs(errs)
+	return serviceInterface, util.JoinErrs(errs), nil
 }
 
 func validateV3(s *tc.ServerV30, tx *sql.Tx) (string, error) {
@@ -743,9 +824,14 @@ func Read(w http.ResponseWriter, r *http.Request) {
 	if version.Major >= 3 {
 		v3Servers := make([]tc.ServerV30, 0)
 		for _, server := range servers {
-			v3Server, err := server.ToServerV3FromV4()
+			csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx)
+			if err != nil {
+				api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to get common server properties from V4 server struct: %w", err))
+				return
+			}
+			v3Server, err := server.ToServerV3FromV4(csp)
 			if err != nil {
-				api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to convert servers to V3 format: %v", err))
+				api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to convert servers to V3 format: %w", err))
 				return
 			}
 			v3Servers = append(v3Servers, v3Server)
@@ -756,7 +842,12 @@ func Read(w http.ResponseWriter, r *http.Request) {
 
 	legacyServers := make([]tc.ServerNullableV2, 0, len(servers))
 	for _, server := range servers {
-		legacyServer, err := server.ToServerV2FromV4()
+		csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to get common server properties from V4 server struct: %w", err))
+			return
+		}
+		legacyServer, err := server.ToServerV2FromV4(csp)
 		if err != nil {
 			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to convert servers to legacy format: %v", err))
 			return
@@ -919,8 +1010,45 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth
 	servers := make(map[int]tc.ServerV40)
 	ids := []int{}
 	for rows.Next() {
-		var s tc.ServerV40
-		if err = rows.StructScan(&s); err != nil {
+		s := tc.ServerV40{}
+		err := rows.Scan(&s.Cachegroup,
+			&s.CachegroupID,
+			&s.CDNID,
+			&s.CDNName,
+			&s.DomainName,
+			&s.GUID,
+			&s.HostName,
+			&s.HTTPSPort,
+			&s.ID,
+			&s.ILOIPAddress,
+			&s.ILOIPGateway,
+			&s.ILOIPNetmask,
+			&s.ILOPassword,
+			&s.ILOUsername,
+			&s.LastUpdated,
+			&s.MgmtIPAddress,
+			&s.MgmtIPGateway,
+			&s.MgmtIPNetmask,
+			&s.OfflineReason,
+			&s.PhysLocation,
+			&s.PhysLocationID,
+			pq.Array(&s.ProfileNames),
+			&s.Rack,
+			&s.RevalPending,
+			&s.RevalUpdateTime,
+			&s.RevalApplyTime,
+			&s.Status,
+			&s.StatusID,
+			&s.TCPPort,
+			&s.Type,
+			&s.TypeID,
+			&s.UpdPending,
+			&s.ConfigUpdateTime,
+			&s.ConfigApplyTime,
+			&s.XMPPID,
+			&s.XMPPPasswd,
+			&s.StatusLastUpdated)
+		if err != nil {
 			return nil, serverCount, nil, errors.New("getting servers: " + err.Error()), http.StatusInternalServerError, nil
 		}
 		if user.PrivLevel < auth.PrivLevelOperations {
@@ -1105,7 +1233,43 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID
 	ids := []int{}
 	for rows.Next() {
 		var s tc.ServerV40
-		if err := rows.StructScan(&s); err != nil {
+		if err := rows.Scan(&s.Cachegroup,
+			&s.CachegroupID,
+			&s.CDNID,
+			&s.CDNName,
+			&s.DomainName,
+			&s.GUID,
+			&s.HostName,
+			&s.HTTPSPort,
+			&s.ID,
+			&s.ILOIPAddress,
+			&s.ILOIPGateway,
+			&s.ILOIPNetmask,
+			&s.ILOPassword,
+			&s.ILOUsername,
+			&s.LastUpdated,
+			&s.MgmtIPAddress,
+			&s.MgmtIPGateway,
+			&s.MgmtIPNetmask,
+			&s.OfflineReason,
+			&s.PhysLocation,
+			&s.PhysLocationID,
+			pq.Array(&s.ProfileNames),
+			&s.Rack,
+			&s.RevalPending,
+			&s.RevalUpdateTime,
+			&s.RevalApplyTime,
+			&s.Status,
+			&s.StatusID,
+			&s.TCPPort,
+			&s.Type,
+			&s.TypeID,
+			&s.UpdPending,
+			&s.ConfigUpdateTime,
+			&s.ConfigApplyTime,
+			&s.XMPPID,
+			&s.XMPPPasswd,
+			&s.StatusLastUpdated); err != nil {
 			log.Errorf("could not scan mid servers: %s\n", err)
 			return nil, nil, err, http.StatusInternalServerError
 		}
@@ -1126,7 +1290,7 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID
 	return ids, nil, nil, http.StatusOK
 }
 
-func checkTypeChangeSafety(server tc.CommonServerProperties, tx *sqlx.Tx) (error, error, int) {
+func checkTypeChangeSafety(server tc.ServerV40, tx *sqlx.Tx) (error, error, int) {
 	// see if cdn or type changed
 	var cdnID int
 	var typeID int
@@ -1317,9 +1481,18 @@ func Update(w http.ResponseWriter, r *http.Request) {
 			server.StatusLastUpdated = original.StatusLastUpdated
 			statusLastUpdatedTime = *original.StatusLastUpdated
 		}
-		_, err := validateV4(&server, tx)
-		if err != nil {
-			api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+		_, userErr, sysErr := validateV4(&server, tx)
+		if userErr != nil || sysErr != nil {
+			errCode := http.StatusBadRequest
+			if sysErr != nil {
+				errCode = http.StatusInternalServerError
+			}
+			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+			return
+		}
+		if err := dbhelpers.UpdateServerProfilesForV4(*server.ID, server.ProfileNames, tx); err != nil {
+			userErr, sysErr, errCode := api.ParseDBError(err)
+			api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 			return
 		}
 	} else if inf.Version.Major >= 3 {
@@ -1340,7 +1513,15 @@ func Update(w http.ResponseWriter, r *http.Request) {
 			api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
 			return
 		}
-		server, err = serverV3.UpgradeToV40()
+		profileName, err := dbhelpers.UpdateServerProfileTableForV2V3(serverV3.ID, serverV3.Profile, (original.ProfileNames)[0], tx)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err))
+			return
+		}
+		if len(profileName) > 1 {
+			profileName = []string{profileName[0]}
+		}
+		server, err = serverV3.UpgradeToV40(profileName)
 		if err != nil {
 			sysErr = fmt.Errorf("error upgrading valid V3 server to V4 structure: %v", err)
 			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr)
@@ -1357,8 +1538,15 @@ func Update(w http.ResponseWriter, r *http.Request) {
 			api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
 			return
 		}
-
-		server, err = legacyServer.UpgradeToV40()
+		profileName, err := dbhelpers.UpdateServerProfileTableForV2V3(legacyServer.ID, legacyServer.Profile, (original.ProfileNames)[0], tx)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err))
+			return
+		}
+		if len(profileName) > 1 {
+			profileName = []string{profileName[0]}
+		}
+		server, err = legacyServer.UpgradeToV40(profileName)
 		if err != nil {
 			sysErr = fmt.Errorf("error upgrading valid V2 server to V3 structure: %v", err)
 			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr)
@@ -1418,7 +1606,7 @@ func Update(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	if userErr, sysErr, errCode = checkTypeChangeSafety(server.CommonServerProperties, inf.Tx); userErr != nil || sysErr != nil {
+	if userErr, sysErr, errCode = checkTypeChangeSafety(server, inf.Tx); userErr != nil || sysErr != nil {
 		api.HandleErr(w, r, tx, errCode, userErr, sysErr)
 		return
 	}
@@ -1467,7 +1655,44 @@ func Update(w http.ResponseWriter, r *http.Request) {
 	where := `WHERE s.id = $1`
 	selquery := selectQuery + where
 	var srvr tc.ServerV40
-	if err := inf.Tx.QueryRowx(selquery, serverID).StructScan(&srvr); err != nil {
+	err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup,
+		&srvr.CachegroupID,
+		&srvr.CDNID,
+		&srvr.CDNName,
+		&srvr.DomainName,
+		&srvr.GUID,
+		&srvr.HostName,
+		&srvr.HTTPSPort,
+		&srvr.ID,
+		&srvr.ILOIPAddress,
+		&srvr.ILOIPGateway,
+		&srvr.ILOIPNetmask,
+		&srvr.ILOPassword,
+		&srvr.ILOUsername,
+		&srvr.LastUpdated,
+		&srvr.MgmtIPAddress,
+		&srvr.MgmtIPGateway,
+		&srvr.MgmtIPNetmask,
+		&srvr.OfflineReason,
+		&srvr.PhysLocation,
+		&srvr.PhysLocationID,
+		pq.Array(&srvr.ProfileNames),
+		&srvr.Rack,
+		&srvr.RevalPending,
+		&srvr.RevalUpdateTime,
+		&srvr.RevalApplyTime,
+		&srvr.Status,
+		&srvr.StatusID,
+		&srvr.TCPPort,
+		&srvr.Type,
+		&srvr.TypeID,
+		&srvr.UpdPending,
+		&srvr.ConfigUpdateTime,
+		&srvr.ConfigApplyTime,
+		&srvr.XMPPID,
+		&srvr.XMPPPasswd,
+		&srvr.StatusLastUpdated)
+	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
 	}
@@ -1490,7 +1715,16 @@ func Update(w http.ResponseWriter, r *http.Request) {
 		}
 		api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvr)
 	} else {
-		v2Server, err := srvr.ToServerV2FromV4()
+		csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx)
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to get common server properties from V4 server struct: %w", err))
+			return
+		}
+		if err != nil {
+			api.HandleErr(w, r, tx, http.StatusBadRequest, nil, fmt.Errorf("failed to update server_profile: %w", err))
+			return
+		}
+		v2Server, err := srvr.ToServerV2FromV4(csp)
 		if err != nil {
 			sysErr = fmt.Errorf("converting valid v3 server to a v2 structure: %v", err)
 			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr)
@@ -1519,7 +1753,35 @@ func updateServer(tx *sqlx.Tx, server tc.ServerV40) (int64, int, error, error) {
 	var serverId int64
 	rowsAffected := 0
 	for rows.Next() {
-		if err := rows.Scan(&serverId); err != nil {
+		if err := rows.Scan(&server.Cachegroup,
+			&server.CachegroupID,
+			&server.CDNID,
+			&server.CDNName,
+			&server.DomainName,
+			&server.GUID,
+			&server.HostName,
+			&server.HTTPSPort,
+			&serverId,
+			&server.ILOIPAddress,
+			&server.ILOIPGateway,
+			&server.ILOIPNetmask,
+			&server.ILOPassword,
+			&server.ILOUsername,
+			&server.LastUpdated,
+			&server.MgmtIPAddress,
+			&server.MgmtIPGateway,
+			&server.MgmtIPNetmask,
+			&server.OfflineReason,
+			&server.PhysLocation,
+			&server.PhysLocationID,
+			pq.Array(&server.ProfileNames),
+			&server.Rack,
+			&server.Status,
+			&server.StatusID,
+			&server.TCPPort,
+			&server.Type,
+			&server.TypeID,
+			&server.StatusLastUpdated); err != nil {
 			return 0, http.StatusNotFound, nil, fmt.Errorf("scanning lastUpdated from server insert: %v", err)
 		}
 		rowsAffected++
@@ -1535,6 +1797,26 @@ func updateServer(tx *sqlx.Tx, server tc.ServerV40) (int64, int, error, error) {
 	return serverId, http.StatusOK, nil, nil
 }
 
+func insertServerProfile(id int, pName []string, tx *sql.Tx) (error, error, int) {
+	priority := make([]int, 0, len(pName))
+	for i, _ := range pName {
+		priority = append(priority, i)
+	}
+	insertQuery := `
+	INSERT INTO server_profile (
+		server, 
+		profile_name, 
+		priority
+	)SELECT $1, profile_name, priority
+	FROM UNNEST($2::text[], $3::int[]) WITH ORDINALITY AS tmp(profile_name, priority)
+	`
+
+	if _, err := tx.Exec(insertQuery, id, pq.Array(pName), pq.Array(priority)); err != nil {
+		return api.ParseDBError(err)
+	}
+	return nil, nil, http.StatusOK
+}
+
 func createV2(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 	var server tc.ServerNullableV2
 
@@ -1577,6 +1859,7 @@ func createV2(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
+	origProfiles := []string{*server.Profile}
 	serverID, err := createServerV2(inf.Tx, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -1595,20 +1878,68 @@ func createV2(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	where := `WHERE s.id = $1`
 	selquery := selectQuery + where
 	var s4 tc.ServerV40
-	if err := inf.Tx.QueryRowx(selquery, serverID).StructScan(&s4); err != nil {
+	err = inf.Tx.QueryRow(selquery, serverID).Scan(&s4.Cachegroup,
+		&s4.CachegroupID,
+		&s4.CDNID,
+		&s4.CDNName,
+		&s4.DomainName,
+		&s4.GUID,
+		&s4.HostName,
+		&s4.HTTPSPort,
+		&s4.ID,
+		&s4.ILOIPAddress,
+		&s4.ILOIPGateway,
+		&s4.ILOIPNetmask,
+		&s4.ILOPassword,
+		&s4.ILOUsername,
+		&s4.LastUpdated,
+		&s4.MgmtIPAddress,
+		&s4.MgmtIPGateway,
+		&s4.MgmtIPNetmask,
+		&s4.OfflineReason,
+		&s4.PhysLocation,
+		&s4.PhysLocationID,
+		pq.Array(&s4.ProfileNames),
+		&s4.Rack,
+		&s4.RevalPending,
+		&s4.RevalUpdateTime,
+		&s4.RevalApplyTime,
+		&s4.Status,
+		&s4.StatusID,
+		&s4.TCPPort,
+		&s4.Type,
+		&s4.TypeID,
+		&s4.UpdPending,
+		&s4.ConfigUpdateTime,
+		&s4.ConfigApplyTime,
+		&s4.XMPPID,
+		&s4.XMPPPasswd,
+		&s4.StatusLastUpdated)
+	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
 	}
 	s4.Interfaces = ifaces
 
-	srvr, err := s4.ToServerV2FromV4()
+	csp, err := dbhelpers.GetCommonServerPropertiesFromV4(s4, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+	}
+	srvr, err := s4.ToServerV2FromV4(csp)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
 	}
+
 	alerts := tc.CreateAlerts(tc.SuccessLevel, "server was created.")
 	api.WriteAlertsObj(w, r, http.StatusOK, alerts, srvr)
 
@@ -1647,6 +1978,7 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 
 	currentTime := time.Now()
 	server.StatusLastUpdated = &currentTime
+	origProfiles := []string{*server.Profile}
 
 	if server.CDNName != nil {
 		userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName)
@@ -1680,17 +2012,64 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	where := `WHERE s.id = $1`
 	selquery := selectQuery + where
 	var s4 tc.ServerV40
-	if err := inf.Tx.QueryRowx(selquery, serverID).StructScan(&s4); err != nil {
+	err = inf.Tx.QueryRow(selquery, serverID).Scan(&s4.Cachegroup,
+		&s4.CachegroupID,
+		&s4.CDNID,
+		&s4.CDNName,
+		&s4.DomainName,
+		&s4.GUID,
+		&s4.HostName,
+		&s4.HTTPSPort,
+		&s4.ID,
+		&s4.ILOIPAddress,
+		&s4.ILOIPGateway,
+		&s4.ILOIPNetmask,
+		&s4.ILOPassword,
+		&s4.ILOUsername,
+		&s4.LastUpdated,
+		&s4.MgmtIPAddress,
+		&s4.MgmtIPGateway,
+		&s4.MgmtIPNetmask,
+		&s4.OfflineReason,
+		&s4.PhysLocation,
+		&s4.PhysLocationID,
+		pq.Array(&s4.ProfileNames),
+		&s4.Rack,
+		&s4.RevalPending,
+		&s4.RevalUpdateTime,
+		&s4.RevalApplyTime,
+		&s4.Status,
+		&s4.StatusID,
+		&s4.TCPPort,
+		&s4.Type,
+		&s4.TypeID,
+		&s4.UpdPending,
+		&s4.ConfigUpdateTime,
+		&s4.ConfigApplyTime,
+		&s4.XMPPID,
+		&s4.XMPPPasswd,
+		&s4.StatusLastUpdated)
+	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
 	}
 
 	s4.Interfaces = interfaces
 
-	srvr, err := s4.ToServerV3FromV4()
+	csp, err := dbhelpers.GetCommonServerPropertiesFromV4(s4, inf.Tx.Tx)
+	if err != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
+	}
+	srvr, err := s4.ToServerV3FromV4(csp)
 	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
@@ -1726,9 +2105,13 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 
 	server.XMPPID = newUUID()
 
-	_, err := validateV4(&server, inf.Tx.Tx)
-	if err != nil {
-		api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil)
+	_, userErr, sysErr := validateV4(&server, inf.Tx.Tx)
+	if userErr != nil || sysErr != nil {
+		errCode := http.StatusBadRequest
+		if sysErr != nil {
+			errCode = http.StatusInternalServerError
+		}
+		api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
 		return
 	}
 
@@ -1749,6 +2132,7 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
+	origProfiles := server.ProfileNames
 	serverID, err := createServerV4(inf.Tx, server)
 	if err != nil {
 		userErr, sysErr, errCode := api.ParseDBError(err)
@@ -1762,10 +2146,53 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx)
+	if userErr != nil || sysErr != nil {
+		api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+		return
+	}
+
 	where := `WHERE s.id = $1`
 	selquery := selectQuery + where
 	var srvr tc.ServerV40
-	if err := inf.Tx.QueryRowx(selquery, serverID).StructScan(&srvr); err != nil {
+	err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup,
+		&srvr.CachegroupID,
+		&srvr.CDNID,
+		&srvr.CDNName,
+		&srvr.DomainName,
+		&srvr.GUID,
+		&srvr.HostName,
+		&srvr.HTTPSPort,
+		&srvr.ID,
+		&srvr.ILOIPAddress,
+		&srvr.ILOIPGateway,
+		&srvr.ILOIPNetmask,
+		&srvr.ILOPassword,
+		&srvr.ILOUsername,
+		&srvr.LastUpdated,
+		&srvr.MgmtIPAddress,
+		&srvr.MgmtIPGateway,
+		&srvr.MgmtIPNetmask,
+		&srvr.OfflineReason,
+		&srvr.PhysLocation,
+		&srvr.PhysLocationID,
+		pq.Array(&srvr.ProfileNames),
+		&srvr.Rack,
+		&srvr.RevalPending,
+		&srvr.RevalUpdateTime,
+		&srvr.RevalApplyTime,
+		&srvr.Status,
+		&srvr.StatusID,
+		&srvr.TCPPort,
+		&srvr.Type,
+		&srvr.TypeID,
+		&srvr.UpdPending,
+		&srvr.ConfigUpdateTime,
+		&srvr.ConfigApplyTime,
+		&srvr.XMPPID,
+		&srvr.XMPPPasswd,
+		&srvr.StatusLastUpdated)
+	if err != nil {
 		api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err)
 		return
 	}
@@ -1781,7 +2208,18 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) {
 }
 
 func createServerV4(tx *sqlx.Tx, server tc.ServerV40) (int64, error) {
-	rows, err := tx.NamedQuery(insertQueryV4, server)
+	//rows, err := tx.NamedQuery(insertQueryV4, server)
+	var profileID int
+	err := tx.QueryRow("SELECT id FROM profile p WHERE name=$1", (server.ProfileNames)[0]).Scan(&profileID)
+	if err != nil {
+		return 0, fmt.Errorf("unable to get profileID for a profile name: %w", err)
+	}
+
+	rows, err := tx.Query(insertQueryV4, server.CachegroupID, server.CDNID, server.DomainName,
+		server.HostName, server.HTTPSPort, server.ILOIPAddress, server.ILOIPNetmask, server.ILOIPGateway,
+		server.ILOUsername, server.ILOPassword, server.MgmtIPAddress, server.MgmtIPNetmask, server.MgmtIPGateway,
+		server.OfflineReason, server.PhysLocationID, profileID, server.Rack, server.StatusID,
+		server.TCPPort, server.TypeID, server.XMPPID, server.XMPPPasswd)
 	if err != nil {
 		return 0, err
 	}
@@ -1791,7 +2229,35 @@ func createServerV4(tx *sqlx.Tx, server tc.ServerV40) (int64, error) {
 	var serverID int64
 	for rows.Next() {
 		rowsAffected++
-		if err := rows.Scan(&serverID); err != nil {
+		err := rows.Scan(&server.Cachegroup,
+			&server.CachegroupID,
+			&server.CDNID,
+			&server.CDNName,
+			&server.DomainName,
+			&server.GUID,
+			&server.HostName,
+			&server.HTTPSPort,
+			&serverID,
+			&server.ILOIPAddress,
+			&server.ILOIPGateway,
+			&server.ILOIPNetmask,
+			&server.ILOPassword,
+			&server.ILOUsername,
+			&server.LastUpdated,
+			&server.MgmtIPAddress,
+			&server.MgmtIPGateway,
+			&server.MgmtIPNetmask,
+			&server.OfflineReason,
+			&server.PhysLocation,
+			&server.PhysLocationID,
+			pq.Array(&server.ProfileNames),
+			&server.Rack,
+			&server.Status,
+			&server.StatusID,
+			&server.TCPPort,
+			&server.Type,
+			&server.TypeID)
+		if err != nil {
 			return 0, err
 		}
 	}
@@ -2027,8 +2493,13 @@ func Delete(w http.ResponseWriter, r *http.Request) {
 	if inf.Version.Major >= 3 {
 		api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server)
 	} else {
-
-		serverV2, err := server.ToServerV2FromV4()
+		csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx)
+		if err != nil {
+			userErr, sysErr, errCode := api.ParseDBError(err)
+			api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+			return
+		}
+		serverV2, err := server.ToServerV2FromV4(csp)
 		if err != nil {
 			api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err)
 			return
diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go
index 4cbf215f36..94b33aa2a6 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_test.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_test.go
@@ -20,7 +20,9 @@ package server
  */
 
 import (
+	"fmt"
 	"net/http"
+	"strings"
 	"testing"
 	"time"
 
@@ -29,74 +31,64 @@ import (
 
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
-	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/test"
-
 	"github.com/jmoiron/sqlx"
 	"gopkg.in/DATA-DOG/go-sqlmock.v1"
 )
 
 type ServerAndInterfaces struct {
-	Server    tc.Server
+	Server    tc.ServerV40
 	Interface tc.ServerInterfaceInfoV40
 }
 
 func getTestServers() []ServerAndInterfaces {
 	servers := []ServerAndInterfaces{}
-	testServer := tc.Server{
-		Cachegroup:     "Cachegroup",
-		CachegroupID:   1,
-		CDNID:          1,
-		CDNName:        "cdnName",
-		DomainName:     "domainName",
-		GUID:           "guid",
-		HostName:       "server1",
-		HTTPSPort:      443,
-		ID:             1,
-		ILOIPAddress:   "iloIpAddress",
-		ILOIPGateway:   "iloIpGateway",
-		ILOIPNetmask:   "iloIpNetmask",
-		ILOPassword:    "iloPassword",
-		ILOUsername:    "iloUsername",
-		InterfaceMtu:   9500,
-		InterfaceName:  "interfaceName",
-		IP6Address:     "ip6Address",
-		IP6IsService:   false,
-		IP6Gateway:     "ip6Gateway",
-		IPAddress:      "ipAddress",
-		IPIsService:    true,
-		IPGateway:      "ipGateway",
-		IPNetmask:      "ipNetmask",
-		LastUpdated:    tc.TimeNoMod{Time: time.Now()},
-		MgmtIPAddress:  "mgmtIpAddress",
-		MgmtIPGateway:  "mgmtIpGateway",
-		MgmtIPNetmask:  "mgmtIpNetmask",
-		OfflineReason:  "offlineReason",
-		PhysLocation:   "physLocation",
-		PhysLocationID: 1,
-		Profile:        "profile",
-		ProfileDesc:    "profileDesc",
-		ProfileID:      1,
-		Rack:           "rack",
-		RevalPending:   true,
-		RouterHostName: "routerHostName",
-		RouterPortName: "routerPortName",
-		Status:         "status",
-		StatusID:       1,
-		TCPPort:        80,
-		Type:           "EDGE",
-		TypeID:         1,
-		UpdPending:     true,
-		XMPPID:         "xmppId",
-		XMPPPasswd:     "xmppPasswd",
-	}
-
-	mtu := uint64(testServer.InterfaceMtu)
+	testServer := tc.ServerV40{
+		Cachegroup:        util.StrPtr("Cachegroup"),
+		CachegroupID:      util.IntPtr(1),
+		CDNID:             util.IntPtr(1),
+		CDNName:           util.StrPtr("cdnName"),
+		DomainName:        util.StrPtr("domainName"),
+		GUID:              util.StrPtr("guid"),
+		HostName:          util.StrPtr("server1"),
+		HTTPSPort:         util.IntPtr(443),
+		ID:                util.IntPtr(1),
+		ILOIPAddress:      util.StrPtr("iloIpAddress"),
+		ILOIPGateway:      util.StrPtr("iloIpGateway"),
+		ILOIPNetmask:      util.StrPtr("iloIpNetmask"),
+		ILOPassword:       util.StrPtr("iloPassword"),
+		ILOUsername:       util.StrPtr("iloUsername"),
+		LastUpdated:       &tc.TimeNoMod{Time: time.Now()},
+		MgmtIPAddress:     util.StrPtr("mgmtIpAddress"),
+		MgmtIPGateway:     util.StrPtr("mgmtIpGateway"),
+		MgmtIPNetmask:     util.StrPtr("mgmtIpNetmask"),
+		OfflineReason:     util.StrPtr("offlineReason"),
+		PhysLocation:      util.StrPtr("physLocation"),
+		PhysLocationID:    util.IntPtr(1),
+		ProfileNames:      []string{"profile"},
+		Rack:              util.StrPtr("rack"),
+		RevalPending:      util.BoolPtr(true),
+		Status:            util.StrPtr("status"),
+		StatusID:          util.IntPtr(1),
+		TCPPort:           util.IntPtr(80),
+		Type:              "EDGE",
+		TypeID:            util.IntPtr(1),
+		UpdPending:        util.BoolPtr(true),
+		XMPPID:            util.StrPtr("xmppId"),
+		XMPPPasswd:        util.StrPtr("xmppPasswd"),
+		StatusLastUpdated: &(time.Time{}),
+		ConfigUpdateTime:  &(time.Time{}),
+		ConfigApplyTime:   &(time.Time{}),
+		RevalUpdateTime:   &(time.Time{}),
+		RevalApplyTime:    &(time.Time{}),
+	}
+
+	mtu := uint64(9500)
 
 	iface := tc.ServerInterfaceInfoV40{
 		ServerInterfaceInfo: tc.ServerInterfaceInfo{
 			IPAddresses: []tc.ServerIPAddress{
 				{
-					Address:        testServer.IPAddress,
+					Address:        "ip6Address",
 					Gateway:        nil,
 					ServiceAddress: true,
 				},
@@ -104,24 +96,24 @@ func getTestServers() []ServerAndInterfaces {
 			MaxBandwidth: nil,
 			Monitor:      true,
 			MTU:          &mtu,
-			Name:         testServer.InterfaceName,
+			Name:         "interfaceName",
 		},
-		RouterHostName: testServer.RouterHostName,
-		RouterPortName: testServer.RouterPortName,
+		RouterHostName: "routerHostName",
+		RouterPortName: "routerPortName",
 	}
 
 	servers = append(servers, ServerAndInterfaces{Server: testServer, Interface: iface})
 
 	testServer2 := testServer
-	testServer2.Cachegroup = "cachegroup2"
-	testServer2.HostName = "server2"
-	testServer2.ID = 2
+	testServer2.Cachegroup = util.StrPtr("cachegroup2")
+	testServer2.HostName = util.StrPtr("server2")
+	testServer2.ID = util.IntPtr(2)
 	servers = append(servers, ServerAndInterfaces{Server: testServer2, Interface: iface})
 
 	testServer3 := testServer
-	testServer3.Cachegroup = "cachegroup3"
-	testServer3.HostName = "server3"
-	testServer3.ID = 3
+	testServer3.Cachegroup = util.StrPtr("cachegroup3")
+	testServer3.HostName = util.StrPtr("server3")
+	testServer3.ID = util.IntPtr(3)
 	servers = append(servers, ServerAndInterfaces{Server: testServer3, Interface: iface})
 
 	return servers
@@ -142,7 +134,7 @@ func TestUpdateServer(t *testing.T) {
 
 	rows := sqlmock.NewRows([]string{"type", "cdn_id"})
 	// note here that the cdnid is 5, which is not the same as the initial cdnid of the fist traffic server
-	rows.AddRow(testServers[0].Server.TypeID, 5)
+	rows.AddRow(*testServers[0].Server.TypeID, 5)
 	// Make it return a list of atleast one associated ds
 	dsrows := sqlmock.NewRows([]string{"array"})
 	dsrows.AddRow("{3}")
@@ -150,11 +142,11 @@ func TestUpdateServer(t *testing.T) {
 	mock.ExpectQuery("SELECT").WillReturnRows(rows)
 	mock.ExpectQuery("SELECT ARRAY").WillReturnRows(dsrows)
 
-	s := tc.CommonServerProperties{
-		CDNID:    &testServers[0].Server.CDNID,
+	s := tc.ServerV40{
+		CDNID:    testServers[0].Server.CDNID,
 		FqdnTime: time.Time{},
-		TypeID:   &testServers[0].Server.TypeID,
-		ID:       &testServers[0].Server.ID,
+		TypeID:   testServers[0].Server.TypeID,
+		ID:       testServers[0].Server.ID,
 	}
 
 	userErr, _, errCode := checkTypeChangeSafety(s, db.MustBegin())
@@ -182,7 +174,12 @@ func TestGetServersByCachegroup(t *testing.T) {
 	unfilteredCols := []string{"count"}
 	unfilteredRows := sqlmock.NewRows(unfilteredCols).AddRow(len(testServers))
 
-	cols := test.ColsFromStructByTag("db", tc.CommonServerProperties{})
+	cols := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name",
+		"https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username",
+		"last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location",
+		"phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time",
+		"status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time",
+		"config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"}
 	interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"}
 	rows := sqlmock.NewRows(cols)
 	interfaceRows := sqlmock.NewRows(interfaceCols)
@@ -195,49 +192,52 @@ func TestGetServersByCachegroup(t *testing.T) {
 	for _, srv := range testServers {
 		ts := srv.Server
 		rows = rows.AddRow(
-			ts.Cachegroup,
-			ts.CachegroupID,
-			ts.CDNID,
-			ts.CDNName,
-			ts.DomainName,
-			ts.GUID,
-			ts.HostName,
-			ts.HTTPSPort,
-			ts.ID,
-			ts.ILOIPAddress,
-			ts.ILOIPGateway,
-			ts.ILOIPNetmask,
-			ts.ILOPassword,
-			ts.ILOUsername,
-			ts.LastUpdated,
-			ts.MgmtIPAddress,
-			ts.MgmtIPGateway,
-			ts.MgmtIPNetmask,
-			ts.OfflineReason,
-			ts.PhysLocation,
-			ts.PhysLocationID,
-			ts.Profile,
-			ts.ProfileDesc,
-			ts.ProfileID,
-			ts.Rack,
-			ts.RevalPending,
-			ts.Status,
-			ts.StatusID,
-			ts.TCPPort,
+			*ts.Cachegroup,
+			*ts.CachegroupID,
+			*ts.CDNID,
+			*ts.CDNName,
+			*ts.DomainName,
+			*ts.GUID,
+			*ts.HostName,
+			*ts.HTTPSPort,
+			*ts.ID,
+			*ts.ILOIPAddress,
+			*ts.ILOIPGateway,
+			*ts.ILOIPNetmask,
+			*ts.ILOPassword,
+			*ts.ILOUsername,
+			*ts.LastUpdated,
+			*ts.MgmtIPAddress,
+			*ts.MgmtIPGateway,
+			*ts.MgmtIPNetmask,
+			*ts.OfflineReason,
+			*ts.PhysLocation,
+			*ts.PhysLocationID,
+			fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")),
+			*ts.Rack,
+			*ts.RevalPending,
+			*ts.RevalUpdateTime,
+			*ts.RevalApplyTime,
+			*ts.Status,
+			*ts.StatusID,
+			*ts.TCPPort,
 			ts.Type,
-			ts.TypeID,
-			ts.UpdPending,
-			ts.XMPPID,
-			ts.XMPPPasswd,
+			*ts.TypeID,
+			*ts.UpdPending,
+			*ts.ConfigUpdateTime,
+			*ts.ConfigApplyTime,
+			*ts.XMPPID,
+			*ts.XMPPPasswd,
+			*ts.StatusLastUpdated,
 		)
 		interfaceRows = interfaceRows.AddRow(
 			srv.Interface.MaxBandwidth,
 			srv.Interface.Monitor,
 			srv.Interface.MTU,
 			srv.Interface.Name,
-			ts.ID,
-			srv.Server.RouterHostName,
-			srv.Server.RouterPortName,
+			*ts.ID,
+			srv.Interface.RouterHostName,
+			srv.Interface.RouterPortName,
 		)
 
 		for _, ip := range srv.Interface.IPAddresses {
@@ -245,7 +245,7 @@ func TestGetServersByCachegroup(t *testing.T) {
 				ip.Address,
 				ip.Gateway,
 				ip.ServiceAddress,
-				ts.ID,
+				*ts.ID,
 				srv.Interface.Name,
 			)
 		}
@@ -288,14 +288,19 @@ func TestGetMidServers(t *testing.T) {
 	testServers := getTestServers()
 	testServers = testServers[0:2]
 
-	testServers[1].Server.Cachegroup = "parentCacheGroup"
-	testServers[1].Server.CachegroupID = 2
+	testServers[1].Server.Cachegroup = util.StrPtr("parentCacheGroup")
+	testServers[1].Server.CachegroupID = util.IntPtr(2)
 	testServers[1].Server.Type = "MID"
 
 	unfilteredCols := []string{"count"}
 	unfilteredRows := sqlmock.NewRows(unfilteredCols).AddRow(len(testServers))
 
-	cols := test.ColsFromStructByTag("db", tc.CommonServerProperties{})
+	cols := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name",
+		"https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username",
+		"last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location",
+		"phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time",
+		"status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time",
+		"config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"}
 	interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"}
 	rows := sqlmock.NewRows(cols)
 	interfaceRows := sqlmock.NewRows(interfaceCols)
@@ -306,49 +311,52 @@ func TestGetMidServers(t *testing.T) {
 	for _, srv := range testServers {
 		ts := srv.Server
 		rows = rows.AddRow(
-			ts.Cachegroup,
-			ts.CachegroupID,
-			ts.CDNID,
-			ts.CDNName,
-			ts.DomainName,
-			ts.GUID,
-			ts.HostName,
-			ts.HTTPSPort,
-			ts.ID,
-			ts.ILOIPAddress,
-			ts.ILOIPGateway,
-			ts.ILOIPNetmask,
-			ts.ILOPassword,
-			ts.ILOUsername,
-			ts.LastUpdated,
-			ts.MgmtIPAddress,
-			ts.MgmtIPGateway,
-			ts.MgmtIPNetmask,
-			ts.OfflineReason,
-			ts.PhysLocation,
-			ts.PhysLocationID,
-			ts.Profile,
-			ts.ProfileDesc,
-			ts.ProfileID,
-			ts.Rack,
-			ts.RevalPending,
-			ts.Status,
-			ts.StatusID,
-			ts.TCPPort,
+			*ts.Cachegroup,
+			*ts.CachegroupID,
+			*ts.CDNID,
+			*ts.CDNName,
+			*ts.DomainName,
+			*ts.GUID,
+			*ts.HostName,
+			*ts.HTTPSPort,
+			*ts.ID,
+			*ts.ILOIPAddress,
+			*ts.ILOIPGateway,
+			*ts.ILOIPNetmask,
+			*ts.ILOPassword,
+			*ts.ILOUsername,
+			*ts.LastUpdated,
+			*ts.MgmtIPAddress,
+			*ts.MgmtIPGateway,
+			*ts.MgmtIPNetmask,
+			*ts.OfflineReason,
+			*ts.PhysLocation,
+			*ts.PhysLocationID,
+			fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")),
+			*ts.Rack,
+			*ts.RevalPending,
+			*ts.RevalUpdateTime,
+			*ts.RevalApplyTime,
+			*ts.Status,
+			*ts.StatusID,
+			*ts.TCPPort,
 			ts.Type,
-			ts.TypeID,
-			ts.UpdPending,
-			ts.XMPPID,
-			ts.XMPPPasswd,
+			*ts.TypeID,
+			*ts.UpdPending,
+			*ts.ConfigUpdateTime,
+			*ts.ConfigApplyTime,
+			*ts.XMPPID,
+			*ts.XMPPPasswd,
+			*ts.StatusLastUpdated,
 		)
 		interfaceRows = interfaceRows.AddRow(
 			srv.Interface.MaxBandwidth,
 			srv.Interface.Monitor,
 			srv.Interface.MTU,
 			srv.Interface.Name,
-			ts.ID,
-			srv.Server.RouterHostName,
-			srv.Server.RouterPortName,
+			*ts.ID,
+			srv.Interface.RouterHostName,
+			srv.Interface.RouterPortName,
 		)
 
 		for _, ip := range srv.Interface.IPAddresses {
@@ -356,7 +364,7 @@ func TestGetMidServers(t *testing.T) {
 				ip.Address,
 				ip.Gateway,
 				ip.ServiceAddress,
-				ts.ID,
+				*ts.ID,
 				srv.Interface.Name,
 			)
 		}
@@ -371,15 +379,19 @@ func TestGetMidServers(t *testing.T) {
 	v := map[string]string{}
 
 	user := auth.CurrentUser{}
-	version := api.Version{Major: 3, Minor: 0}
+	version := api.Version{Major: 4, Minor: 0}
 	servers, _, userErr, sysErr, errCode, _ := getServers(nil, v, db.MustBegin(), &user, false, version)
 
 	if userErr != nil || sysErr != nil {
 		t.Errorf("getServers expected: no errors, actual: %v %v with status: %s", userErr, sysErr, http.StatusText(errCode))
 	}
 
-	cols2 := test.ColsFromStructByTag("db", tc.CommonServerProperties{})
-	cols2 = append(cols2, "config_update_time", "config_apply_time", "revalidate_update_time", "revalidate_apply_time")
+	cols2 := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name",
+		"https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username",
+		"last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location",
+		"phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time",
+		"status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time",
+		"config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"}
 	rows2 := sqlmock.NewRows(cols2)
 
 	cgs := []tc.CacheGroup{}
@@ -444,44 +456,43 @@ func TestGetMidServers(t *testing.T) {
 	}
 	*ts.ID = *ts.ID + 1
 	rows2 = rows2.AddRow(
-		ts.Cachegroup,
-		ts.CachegroupID,
-		ts.CDNID,
-		ts.CDNName,
-		ts.DomainName,
-		ts.GUID,
-		ts.HostName,
-		ts.HTTPSPort,
-		ts.ID,
-		ts.ILOIPAddress,
-		ts.ILOIPGateway,
-		ts.ILOIPNetmask,
-		ts.ILOPassword,
-		ts.ILOUsername,
-		ts.LastUpdated,
-		ts.MgmtIPAddress,
-		ts.MgmtIPGateway,
-		ts.MgmtIPNetmask,
-		ts.OfflineReason,
-		ts.PhysLocation,
-		ts.PhysLocationID,
-		ts.Profile,
-		ts.ProfileDesc,
-		ts.ProfileID,
-		ts.Rack,
-		ts.RevalPending,
-		ts.Status,
-		ts.StatusID,
-		ts.TCPPort,
+		*ts.Cachegroup,
+		*ts.CachegroupID,
+		*ts.CDNID,
+		*ts.CDNName,
+		*ts.DomainName,
+		*ts.GUID,
+		*ts.HostName,
+		*ts.HTTPSPort,
+		*ts.ID,
+		*ts.ILOIPAddress,
+		*ts.ILOIPGateway,
+		*ts.ILOIPNetmask,
+		*ts.ILOPassword,
+		*ts.ILOUsername,
+		*ts.LastUpdated,
+		*ts.MgmtIPAddress,
+		*ts.MgmtIPGateway,
+		*ts.MgmtIPNetmask,
+		*ts.OfflineReason,
+		*ts.PhysLocation,
+		*ts.PhysLocationID,
+		fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")),
+		*ts.Rack,
+		*ts.RevalPending,
+		*ts.RevalUpdateTime,
+		*ts.RevalApplyTime,
+		*ts.Status,
+		*ts.StatusID,
+		*ts.TCPPort,
 		ts.Type,
-		ts.TypeID,
-		ts.UpdPending,
-		ts.XMPPID,
-		ts.XMPPPasswd,
-		ts.ConfigUpdateTime,
-		ts.ConfigApplyTime,
-		ts.RevalUpdateTime,
-		ts.RevalApplyTime,
+		*ts.TypeID,
+		*ts.UpdPending,
+		*ts.ConfigUpdateTime,
+		*ts.ConfigApplyTime,
+		*ts.XMPPID,
+		*ts.XMPPPasswd,
+		*ts.StatusLastUpdated,
 	)
 
 	mock.ExpectBegin()
diff --git a/traffic_ops/v4-client/server.go b/traffic_ops/v4-client/server.go
index 272f15c318..59d3009488 100644
--- a/traffic_ops/v4-client/server.go
+++ b/traffic_ops/v4-client/server.go
@@ -83,18 +83,6 @@ func (to *Session) CreateServer(server tc.ServerV4, opts RequestOptions) (tc.Ale
 		}
 		server.PhysLocationID = &ph.Response[0].ID
 	}
-	if needAndCanFetch(server.ProfileID, server.Profile) {
-		innerOpts := NewRequestOptions()
-		innerOpts.QueryParameters.Set("name", *server.Profile)
-		pr, reqInf, err := to.GetProfiles(innerOpts)
-		if err != nil {
-			return pr.Alerts, reqInf, fmt.Errorf("no Profile named %s: %w", *server.Profile, err)
-		}
-		if len(pr.Response) == 0 {
-			return pr.Alerts, reqInf, fmt.Errorf("no Profile named %s", *server.Profile)
-		}
-		server.ProfileID = &pr.Response[0].ID
-	}
 	if needAndCanFetch(server.StatusID, server.Status) {
 		innerOpts := NewRequestOptions()
 		innerOpts.QueryParameters.Set("name", *server.Status)
diff --git a/traffic_portal/app/src/common/modules/form/server/FormServerController.js b/traffic_portal/app/src/common/modules/form/server/FormServerController.js
index 0e4e16aeda..138956503f 100644
--- a/traffic_portal/app/src/common/modules/form/server/FormServerController.js
+++ b/traffic_portal/app/src/common/modules/form/server/FormServerController.js
@@ -22,6 +22,7 @@ var FormServerController = function(server, $scope, $location, $state, $uibModal
     $scope.IPPattern = serverUtils.IPPattern;
     $scope.IPWithCIDRPattern = serverUtils.IPWithCIDRPattern;
     $scope.IPv4Pattern = serverUtils.IPv4Pattern;
+    $scope.profiles = [];
 
     var getPhysLocations = function() {
         physLocationService.getPhysLocations()
@@ -60,6 +61,14 @@ var FormServerController = function(server, $scope, $location, $state, $uibModal
             });
     };
 
+    $scope.getProfileID = function(profileName) {
+        for (const profile of $scope.profiles) {
+            if (profile.name === profileName) {
+                return "/#!/profiles/"+profile.id
+            }
+        }
+    };
+
     var updateStatus = function(status) {
         serverService.updateStatus(server.id, { status: status.id, offlineReason: status.offlineReason })
             .then(
diff --git a/traffic_portal/app/src/common/modules/form/server/form.server.tpl.html b/traffic_portal/app/src/common/modules/form/server/form.server.tpl.html
index 2fc5182e14..5b29c7ce0b 100644
--- a/traffic_portal/app/src/common/modules/form/server/form.server.tpl.html
+++ b/traffic_portal/app/src/common/modules/form/server/form.server.tpl.html
@@ -112,11 +112,11 @@ under the License.
 
             <label for="profile" ng-class="{'has-error': hasError(serverForm.profile)}">Profile *</label>
             <div ng-class="{'has-error': hasError(serverForm.profile), 'has-feedback': hasError(serverForm.profile)}">
-                <select id="profile" name="profile" class="form-control" ng-model="server.profileId" ng-options="profile.id as profile.name for profile in profiles" required>
+                <select id="profile" name="profile" class="form-control" ng-model="server.profileNames[0]" ng-options="profile.name as profile.name for profile in profiles" required>
                     <option hidden selected disabled value="">Select...</option>
                 </select>
                 <small class="input-error" ng-show="hasPropertyError(serverForm.profile, 'required')">Required</small>
-                <small ng-show="server.profileId"><a ng-href="/#!/profiles/{{server.profileId}}" target="_blank">View Details&nbsp;&nbsp;<i class="fa fs-xs fa-external-link"></i></a></small>
+                <small ng-show="server.profileNames.length>0"><a ng-href="{{getProfileID(server.profileNames[0])}}" target="_blank">View Details&nbsp;&nbsp;<i class="fa fs-xs fa-external-link"></i></a></small>
             </div>
 
             <label for="physLocation" ng-class="{'has-error': hasError(serverForm.physLocation)}">Physical Location *</label>
diff --git a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
index 15b6a265fd..9d3fa86b12 100644
--- a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
+++ b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js
@@ -162,7 +162,7 @@ var TableServersController = function(tableName, servers, filter, $scope, $state
 		},
 		{
 			headerName: "Profile",
-			field: "profile",
+			field: "profileName",
 			hide: false
 		},
 		{
@@ -365,6 +365,9 @@ var TableServersController = function(tableName, servers, filter, $scope, $state
 			x.lastUpdated = x.lastUpdated ? new Date(x.lastUpdated.replace("+00", "Z")) : x.lastUpdated;
 			x.statusLastUpdated = x.statusLastUpdated ? new Date(x.statusLastUpdated): x.statusLastUpdated;
 			Object.assign(x, serverUtils.toLegacyIPInfo(x.interfaces));
+			if (x.profileNames !== undefined) {
+				x.profileName = x.profileNames[0]
+			}
 			return x;
 	});
 
diff --git a/traffic_portal/app/src/modules/private/servers/new/index.js b/traffic_portal/app/src/modules/private/servers/new/index.js
index cb725acddf..6e70449257 100644
--- a/traffic_portal/app/src/modules/private/servers/new/index.js
+++ b/traffic_portal/app/src/modules/private/servers/new/index.js
@@ -43,7 +43,8 @@ module.exports = angular.module('trafficPortal.private.servers.new', [])
                                             ],
                                             monitor: true
                                         }
-                                    ]
+                                    ],
+                                    profileNames: []
                                 };
                             }
                         }
diff --git a/traffic_portal/test/integration/CommonUtils/API.ts b/traffic_portal/test/integration/CommonUtils/API.ts
index 8e1a8ebeab..8e4375660c 100644
--- a/traffic_portal/test/integration/CommonUtils/API.ts
+++ b/traffic_portal/test/integration/CommonUtils/API.ts
@@ -337,6 +337,11 @@ export class API {
                 }
             }
         }
+        if(hasProperty(data, 'profileNames', "Array")){
+            for (const index in data.profileNames) {
+                data.profileNames[index] = data.profileNames[index]+randomize
+            }
+        }
     }
 
     public async UseAPI(data: Array<APIData>): Promise<void> {
diff --git a/traffic_portal/test/integration/Data/deliveryservices.ts b/traffic_portal/test/integration/Data/deliveryservices.ts
index 153ea8feb2..c6f9316895 100644
--- a/traffic_portal/test/integration/Data/deliveryservices.ts
+++ b/traffic_portal/test/integration/Data/deliveryservices.ts
@@ -266,7 +266,7 @@ export const deliveryservices = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -291,12 +291,6 @@ export const deliveryservices = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				}
diff --git a/traffic_portal/test/integration/Data/physlocations.ts b/traffic_portal/test/integration/Data/physlocations.ts
index b76aa58187..e6628ddd27 100644
--- a/traffic_portal/test/integration/Data/physlocations.ts
+++ b/traffic_portal/test/integration/Data/physlocations.ts
@@ -201,7 +201,7 @@ export const physLocations = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -226,12 +226,6 @@ export const physLocations = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				}
diff --git a/traffic_portal/test/integration/Data/servers.ts b/traffic_portal/test/integration/Data/servers.ts
index 96f822ad91..58490dea9d 100644
--- a/traffic_portal/test/integration/Data/servers.ts
+++ b/traffic_portal/test/integration/Data/servers.ts
@@ -312,7 +312,7 @@ export const servers = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -337,12 +337,6 @@ export const servers = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -384,7 +378,7 @@ export const servers = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -409,12 +403,6 @@ export const servers = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -456,7 +444,7 @@ export const servers = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -481,12 +469,6 @@ export const servers = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -528,7 +510,7 @@ export const servers = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -553,12 +535,6 @@ export const servers = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				}
diff --git a/traffic_portal/test/integration/Data/serverservercapabilities.ts b/traffic_portal/test/integration/Data/serverservercapabilities.ts
index 46d4371b02..222fd8acef 100644
--- a/traffic_portal/test/integration/Data/serverservercapabilities.ts
+++ b/traffic_portal/test/integration/Data/serverservercapabilities.ts
@@ -269,7 +269,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 0,
-					profileId: 0,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -294,12 +294,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -341,7 +335,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 2,
-					profileId: 9,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -366,12 +360,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -413,7 +401,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 2,
-					profileId: 9,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -438,12 +426,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -485,7 +467,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 2,
-					profileId: 9,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -510,12 +492,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -557,7 +533,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 2,
-					profileId: 9,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -582,12 +558,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				},
@@ -629,7 +599,7 @@ export const serverServerCapabilities = {
 					mgmtIpNetmask: "",
 					offlineReason: "",
 					physLocationId: 2,
-					profileId: 9,
+					profileNames: ["testProfile"],
 					routerHostName: "",
 					routerPortName: "",
 					statusId: 3,
@@ -654,12 +624,6 @@ export const serverServerCapabilities = {
 							queryKey: "name",
 							queryValue: "testCG",
 							replace: "cachegroupId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "testProfile",
-							replace: "profileId"
 						}
 					]
 				}
diff --git a/traffic_portal/test/integration/Data/topologies.ts b/traffic_portal/test/integration/Data/topologies.ts
index 7ad779e5f2..1edc774068 100644
--- a/traffic_portal/test/integration/Data/topologies.ts
+++ b/traffic_portal/test/integration/Data/topologies.ts
@@ -352,7 +352,7 @@ export const topologies = {
                     mgmtIpNetmask: "",
                     offlineReason: "",
                     physLocationId: 2,
-                    profileId: 9,
+                    profileNames: ["TopTestPf"],
                     routerHostName: "",
                     routerPortName: "",
                     statusId: 3,
@@ -376,12 +376,6 @@ export const topologies = {
 							queryKey: "name",
 							queryValue: "TopTestCDN",
 							replace: "cdnId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "TopTestPf",
-							replace: "profileId"
 						}
                     ],
                     updPending: false
@@ -424,7 +418,7 @@ export const topologies = {
                     mgmtIpNetmask: "",
                     offlineReason: "",
                     physLocationId: 2,
-                    profileId: 9,
+                    profileNames: ["TopTestPf"],
                     routerHostName: "",
                     routerPortName: "",
                     statusId: 3,
@@ -448,12 +442,6 @@ export const topologies = {
 							queryKey: "name",
 							queryValue: "TopTestCDN",
 							replace: "cdnId"
-						},
-						{
-							route: "/profiles",
-							queryKey: "name",
-							queryValue: "TopTestPf",
-							replace: "profileId"
 						}
                     ],
                     updPending: false