diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java
index 19d10064b854566cccba009646acff3a92368ddf..ceee46a7218d39cecb3768191db27992e2dfa86a 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java
@@ -19,6 +19,7 @@
 package org.sleuthkit.datamodel;
 
 import com.google.common.base.Strings;
+import com.google.common.annotations.Beta;
 import org.apache.commons.lang3.StringUtils;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -331,6 +332,84 @@ public OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountReal
 			}
 		}
 	}
+	
+	/**
+	 * Creates a local OS account with Linux-specific data. If an account already
+	 * exists with the given id or realm/login, then the existing OS account is
+	 * returned.
+	 *
+	 * @param uid       Account uid, can be null if loginName is supplied.
+	 * @param loginName Login name, can be null if uid is supplied.
+	 * @param referringHost     The associated host.
+	 *
+	 * @return OsAccount.
+	 *
+	 * @throws TskCoreException                     If there is an error in
+	 *                                              creating the OSAccount.
+	 *
+	 */
+	@Beta
+	public OsAccount newLocalLinuxOsAccount(String uid, String loginName, Host referringHost) throws TskCoreException {
+
+		if (referringHost == null) {
+			throw new TskCoreException("A referring host is required to create a local OS account.");
+		}
+		
+		// Ensure at least one of the two is supplied - a non-null unique id or a login name
+		if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
+			throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null.");
+		}
+		
+		OsAccountRealm localRealm = db.getOsAccountRealmManager().newLocalLinuxRealm(referringHost);
+		
+		CaseDbTransaction trans = db.beginTransaction();
+		try {
+			
+			// try to create account
+			try {
+				OsAccount account = newOsAccount(uid, loginName, localRealm, OsAccount.OsAccountStatus.UNKNOWN, trans);
+				trans.commit();
+				trans = null;
+				return account;
+			} catch (SQLException ex) {
+				// Rollback the transaction before proceeding
+				trans.rollback();
+				trans = null;
+
+				// Create may fail if an OsAccount already exists. 
+				Optional<OsAccount> osAccount;
+
+				// First search for account by uniqueId
+				if (!Strings.isNullOrEmpty(uid)) {
+					osAccount = getOsAccountByAddr(uid, localRealm);
+					if (osAccount.isPresent()) {
+						return osAccount.get();
+					}
+				}
+
+				// search by loginName
+				if (!Strings.isNullOrEmpty(loginName)) {
+					osAccount = getOsAccountByLoginName(loginName, localRealm);
+					if (osAccount.isPresent()) {
+						return osAccount.get();
+					}
+				}
+
+				// create failed for some other reason, throw an exception
+				throw new TskCoreException(String.format("Error creating OsAccount with uid = %s, loginName = %s, realm = %s, referring host = %s",
+						(uid != null) ? uid : "Null",
+						(loginName != null) ? loginName : "Null",
+						(!localRealm.getRealmNames().isEmpty()) ? localRealm.getRealmNames().get(0) : "Null",
+						localRealm.getScopeHost().isPresent() ? localRealm.getScopeHost().get().getName() : "Null"), ex);
+
+			}
+		} finally {
+			if (trans != null) {
+				trans.rollback();
+			}
+		}
+	}
+
 
 	/**
 	 * Creates a OS account with the given uid, name, and realm.
@@ -426,7 +505,7 @@ private Optional<OsAccount> getOsAccountByAddr(String uniqueId, Host host, CaseD
 
 		String queryString = "SELECT accounts.os_account_obj_id as os_account_obj_id, accounts.login_name, accounts.full_name, "
 				+ " accounts.realm_id, accounts.addr, accounts.signature, "
-				+ "	accounts.type, accounts.status, accounts.admin, accounts.created_date, accounts.db_status, "
+				+ "	accounts.type, accounts.status, accounts.created_date, accounts.db_status, "
 				+ " realms.realm_name as realm_name, realms.realm_addr as realm_addr, realms.realm_signature, realms.scope_host_id, realms.scope_confidence, realms.db_status as realm_db_status "
 				+ " FROM tsk_os_accounts as accounts"
 				+ "		LEFT JOIN tsk_os_account_realms as realms"
@@ -1212,6 +1291,52 @@ public Optional<OsAccount> getWindowsOsAccount(String sid, String loginName, Str
 		}
 	}
 
+	/**
+	 * Gets an OS account using Linux-specific data.
+	 *
+	 * @param uid           Account UID, maybe null if loginName is supplied.
+	 * @param loginName     Login name, maybe null if sid is supplied.
+	 * @param referringHost Host referring the account.
+	 *
+	 * @return Optional with OsAccount, Optional.empty if no matching OsAccount
+	 *         is found.
+	 *
+	 * @throws TskCoreException    If there is an error getting the account.
+	 */
+	@Beta
+	public Optional<OsAccount> getLocalLinuxOsAccount(String uid, String loginName, Host referringHost) throws TskCoreException {
+
+		if (referringHost == null) {
+			throw new TskCoreException("A referring host is required to get an account.");
+		}
+
+		// ensure at least one of the two is supplied - a non-null uid or a login name
+		if (StringUtils.isBlank(uid) && StringUtils.isBlank(loginName)) {
+			throw new TskCoreException("Cannot get an OS account with both UID and loginName as null.");
+		}
+			
+		// First get the local realm
+		Optional<OsAccountRealm> realm = db.getOsAccountRealmManager().getLocalLinuxRealm(referringHost);
+		if (!realm.isPresent()) {
+			return Optional.empty();
+		}
+
+		// Search by UID
+		if (!Strings.isNullOrEmpty(uid)) {
+			Optional<OsAccount> account = this.getOsAccountByAddr(uid, realm.get());
+			if (account.isPresent()) {
+				return account;
+			}
+		}
+
+		// Search by login name
+		if (!Strings.isNullOrEmpty(loginName)) {
+			return this.getOsAccountByLoginName(loginName, realm.get());
+		} else {
+			return Optional.empty();
+		}
+	}	
+	
 	/**
 	 * Adds a rows to the tsk_os_account_attributes table for the given set of
 	 * attribute.
@@ -1730,6 +1855,70 @@ private OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osA
 		return updateStatus;
 	}
 
+	/**
+	 * Update the address and/or login name for the specified account in the
+	 * database.
+	 *
+	 * A column is updated only if its current value is null and a non-null
+	 * value has been specified.
+	 *
+	 * @param osAccount     OsAccount that needs to be updated in the database.
+	 * @param uid           Account ID, may be null.
+	 * @param loginName     Login name, may be null.
+	 *
+	 * @return OsAccountUpdateResult Account update status, and the updated
+	 *         account.
+	 *
+	 * @throws TskCoreException If there is a database error or if the updated
+	 *                          information conflicts with an existing account.
+	 */
+	public OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName) throws TskCoreException {
+		CaseDbTransaction trans = db.beginTransaction();
+		try {
+			OsAccountUpdateResult updateStatus = this.updateCoreLocalLinuxOsAccountAttributes(osAccount, uid, loginName, trans);
+
+			trans.commit();
+			trans = null;
+			return updateStatus;
+		} finally {
+			if (trans != null) {
+				trans.rollback();
+			}
+		}
+	}
+	
+	/**
+	 * Update the address and/or login name for the specified account in the
+	 * database.
+	 *
+	 * A column is updated only if it's current value is null and a non-null
+	 * value has been specified.
+	 *
+	 * @param osAccount  OsAccount that needs to be updated in the database.
+	 * @param uid        Account ID, may be null.
+	 * @param loginName  Login name, may be null.
+	 *
+	 * @return OsAccountUpdateResult Account update status, and the updated
+	 *         account.
+	 *
+	 * @throws TskCoreException If there is a database error or if the updated
+	 *                          information conflicts with an existing account.
+	 */
+	@Beta
+	private OsAccountUpdateResult updateCoreLocalLinuxOsAccountAttributes(OsAccount osAccount, String uid, String loginName, CaseDbTransaction trans) throws TskCoreException {
+		
+		// Update the account core data
+		OsAccountUpdateResult updateStatus = this.updateOsAccountCore(osAccount, uid, loginName, trans);
+
+		Optional<OsAccount> updatedAccount = updateStatus.getUpdatedAccount();
+		if (updatedAccount.isPresent()) {
+			// After updating account data, check if there is matching account to merge
+			mergeOsAccount(updatedAccount.get(), trans);
+		}
+		
+		return updateStatus;
+	}
+	
 	/**
 	 * Update the address and/or login name for the specified account in the
 	 * database.
@@ -1786,7 +1975,32 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad
 			String newLoginName = currAccount.getLoginName().orElse(null);
 
 			String newSignature = getOsAccountSignature(newAddress, newLoginName);
-			updateAccountSignature(osAccount.getId(), newSignature, connection);
+			
+			try {
+				updateAccountSignature(osAccount.getId(), newSignature, connection);
+			} catch (SQLException ex) {
+				// There's a slight chance that we're in the case where we are trying to add an addr to an OS account
+				// with only a name where the addr already exists on a different OS account. This will cause a unique
+				// constraint failure in updateAccountSignature(). This is unlikely to happen in normal use
+				// since we lookup OS accounts by addr before name when we have both (i.e., it would be strange to have an
+				// OsAccount in hand with only the loginName set when we also know the addr). 
+				// Correctly handling every case here is non-trivial, so for the moment only look for the specific case where
+				// we had an OsAccount with just an addr and and OsAccount with just a login name that we now 
+				// want to combine.
+				if (osAccount.getAddr().isEmpty() && !StringUtils.isBlank(address)) {
+					OsAccountRealm realm = db.getOsAccountRealmManager().getRealmByRealmId(osAccount.getRealmId(), connection);
+					Optional<OsAccount> matchingAddrAcct = getOsAccountByAddr(address, realm.getScopeHost().get(), connection);
+					if (matchingAddrAcct.isEmpty() 
+							|| matchingAddrAcct.get().getId() == osAccount.getId()
+							|| matchingAddrAcct.get().getLoginName().isPresent()) {
+						throw ex; // Rethrow the original error
+					}
+					
+					// What we should have is osAccount with just a loginName and matchingAddrAcct with 
+					// just an address, so merge them.
+					mergeOsAccounts(matchingAddrAcct.get(), osAccount, trans);
+				}
+			}
 
 			// get the updated account from database
 			updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection);
@@ -1796,7 +2010,7 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad
 
 			return new OsAccountUpdateResult(updateStatusCode, updatedAccount);
 
-		} catch (SQLException ex) {
+		} catch (SQLException ex) {			
 			throw new TskCoreException(String.format("Error updating account with unique id = %s, account id = %d", osAccount.getAddr().orElse("Unknown"), osAccount.getId()), ex);
 		}
 	}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java
index 959eb1cc8ab130a5b314e89d694bc59f5a0341fd..2b75c410aeec7267f656f7192b18d3603a98a477 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java
@@ -18,6 +18,7 @@
  */
 package org.sleuthkit.datamodel;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Strings;
 import org.apache.commons.lang3.StringUtils;
 import java.sql.PreparedStatement;
@@ -43,6 +44,7 @@
 public final class OsAccountRealmManager {
 
 	private static final Logger LOGGER = Logger.getLogger(OsAccountRealmManager.class.getName());
+	private static final String LOCAL_REALM_NAME = "local";
 
 	private final SleuthkitCase db;
 
@@ -148,6 +150,52 @@ public OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host
 		return newRealm(resolvedRealmName, realmAddr, signature, scopeHost, scopeConfidence);
 	}
 	
+	/**
+	 * Create local realm to use for Linux accounts.
+	 *
+	 * @param referringHost Host where realm reference is found.
+	 *
+	 * @return OsAccountRealm.
+	 *
+	 * @throws TskCoreException                     If there is an error
+	 *                                              creating the realm.
+	 */
+	@Beta
+	public OsAccountRealm newLocalLinuxRealm(Host referringHost) throws TskCoreException {
+
+		if (referringHost == null) {
+			throw new TskCoreException("A referring host is required to create a realm.");
+		}
+		
+		String realmName = LOCAL_REALM_NAME;
+		OsAccountRealm.ScopeConfidence scopeConfidence = OsAccountRealm.ScopeConfidence.KNOWN;		
+		String signature = makeRealmSignature("", realmName, referringHost);
+		
+		// create a realm
+		return newRealm(realmName, "", signature, referringHost, scopeConfidence);
+	}
+	
+	/**
+	 * Get local realm to use for Linux accounts.
+	 *
+	 * @param referringHost Host where realm reference is found.
+	 *
+	 * @return OsAccountRealm.
+	 *
+	 * @throws TskCoreException                     If there is an error
+	 *                                              creating the realm.
+	 */
+	@Beta
+	public Optional<OsAccountRealm> getLocalLinuxRealm(Host referringHost) throws TskCoreException {
+		if (referringHost == null) {
+			throw new TskCoreException("A referring host is required get a realm.");
+		}
+		
+		try (CaseDbConnection connection = this.db.getConnection()) {
+			return getRealmByName(LOCAL_REALM_NAME, referringHost, connection);
+		}
+	}
+	
 	/**
 	 * Get a windows realm by the account SID, or the domain name. The input SID
 	 * is an user/group account SID. The domain SID is extracted from this
diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java
index b18826c9b745bdaad566dd51e4270b7be1dc0bcb..d086cd9feb642c8999624a156c511d976dfc36f9 100644
--- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java
+++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java
@@ -770,6 +770,80 @@ public void basicOsAccountTests() throws TskCoreException, OsAccountManager.NotU
 
 	}
 	
+	@Test
+	public void basicLinuxOsAccountTests() throws TskCoreException {
+
+		try {
+			String hostname1 = "linuxTestHost";
+			Host host1 = caseDB.getHostManager().newHost(hostname1);
+			
+			// Create an account with uid and username
+			String uid1 = "501";
+			String loginName1 = "user1";
+			
+			OsAccount osAccount1 = caseDB.getOsAccountManager().newLocalLinuxOsAccount(uid1, loginName1, host1);
+			assertEquals(osAccount1.getAddr().orElse("").equalsIgnoreCase(uid1), true);
+			assertEquals(osAccount1.getLoginName().orElse("").equalsIgnoreCase(loginName1), true);
+			
+			Optional<OsAccount> osAccount1loadWithUID = caseDB.getOsAccountManager().getLocalLinuxOsAccount(uid1, null, host1);
+			assertEquals(osAccount1loadWithUID.isPresent(), true);
+			assertEquals(osAccount1loadWithUID.get().getAddr().orElse("").equalsIgnoreCase(uid1), true);
+			assertEquals(osAccount1loadWithUID.get().getLoginName().orElse("").equalsIgnoreCase(loginName1), true);
+				
+			// Create an account with only a UID then update it
+			String uid2 = "502";
+			String loginName2 = "user2";
+			
+			OsAccount osAccount2 = caseDB.getOsAccountManager().newLocalLinuxOsAccount(uid2, null, host1);
+			assertEquals(osAccount2.getAddr().orElse("").equalsIgnoreCase(uid2), true);
+			assertEquals(osAccount2.getLoginName().isEmpty(), true);
+			
+			OsAccountManager.OsAccountUpdateResult updateResult = caseDB.getOsAccountManager()
+					.updateCoreLocalLinuxOsAccountAttributes(osAccount2, uid2, loginName2);
+            Optional<OsAccount> oOsAccount2Updated = updateResult.getUpdatedAccount();
+			
+			assertEquals(updateResult.getUpdateStatusCode().equals(OsAccountManager.OsAccountUpdateStatus.UPDATED), true);
+			assertEquals(oOsAccount2Updated.isPresent(), true);
+			assertEquals(oOsAccount2Updated.get().getAddr().orElse("").equalsIgnoreCase(uid2), true);
+			assertEquals(oOsAccount2Updated.get().getLoginName().orElse("").equalsIgnoreCase(loginName2), true);
+			
+			// Test unusual merge case (unusual because we're updating the account with the name, not the UID)
+			String uid3 = "503";
+			String loginName3 = "user3";
+			
+			OsAccount osAccount3uidOnly = caseDB.getOsAccountManager().newLocalLinuxOsAccount(uid3, null, host1);
+			OsAccount osAccount3nameOnly = caseDB.getOsAccountManager().newLocalLinuxOsAccount(null, loginName3, host1);
+			
+			updateResult = caseDB.getOsAccountManager()
+					.updateCoreLocalLinuxOsAccountAttributes(osAccount3nameOnly, uid3, loginName3);
+            Optional<OsAccount> oOsAccount3Updated = updateResult.getUpdatedAccount();
+			
+			assertEquals(updateResult.getUpdateStatusCode().equals(OsAccountManager.OsAccountUpdateStatus.UPDATED), true);
+			assertEquals(oOsAccount3Updated.isPresent(), true);
+			assertEquals(oOsAccount3Updated.get().getAddr().orElse("").equalsIgnoreCase(uid3), true);
+			assertEquals(oOsAccount3Updated.get().getLoginName().orElse("").equalsIgnoreCase(loginName3), true);
+			
+			// Test normal merge case
+			String uid4 = "504";
+			String loginName4 = "user4";
+			
+			OsAccount osAccount4uidOnly = caseDB.getOsAccountManager().newLocalLinuxOsAccount(uid4, null, host1);
+			OsAccount osAccount4nameOnly = caseDB.getOsAccountManager().newLocalLinuxOsAccount(null, loginName4, host1);
+			
+			updateResult = caseDB.getOsAccountManager()
+					.updateCoreLocalLinuxOsAccountAttributes(osAccount4uidOnly, uid4, loginName4);
+            Optional<OsAccount> oOsAccount4Updated = updateResult.getUpdatedAccount();
+			
+			assertEquals(updateResult.getUpdateStatusCode().equals(OsAccountManager.OsAccountUpdateStatus.UPDATED), true);
+			assertEquals(oOsAccount4Updated.isPresent(), true);
+			assertEquals(oOsAccount4Updated.get().getAddr().orElse("").equalsIgnoreCase(uid4), true);
+			assertEquals(oOsAccount4Updated.get().getLoginName().orElse("").equalsIgnoreCase(loginName4), true);
+			
+		} finally {
+			
+		}
+
+	}
 	
 	@Test
 	public void windowsSpecialAccountTests() throws TskCoreException, OsAccountManager.NotUserSIDException {